//
// Copyright (c) 2013-2021 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_protocol.hpp>

using namespace std;

#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_rtmp_msg_array.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_st.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_rtmp_stack.hpp>
#include <srs_service_http_conn.hpp>

MockEmptyIO::MockEmptyIO()
{
}

MockEmptyIO::~MockEmptyIO()
{
}

srs_error_t MockEmptyIO::read_fully(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/)
{
    return srs_success;
}

srs_error_t MockEmptyIO::write(void* /*buf*/, size_t /*size*/, ssize_t* /*nwrite*/)
{
    return srs_success;
}

void MockEmptyIO::set_recv_timeout(srs_utime_t /*tm*/)
{
}

srs_utime_t MockEmptyIO::get_recv_timeout()
{
    return -1;
}

int64_t MockEmptyIO::get_recv_bytes()
{
    return -1;
}

void MockEmptyIO::set_send_timeout(srs_utime_t /*tm*/)
{
}

srs_utime_t MockEmptyIO::get_send_timeout()
{
    return 0;
}

int64_t MockEmptyIO::get_send_bytes()
{
    return 0;
}

srs_error_t MockEmptyIO::writev(const iovec */*iov*/, int /*iov_size*/, ssize_t* /*nwrite*/)
{
    return srs_success;
}

srs_error_t MockEmptyIO::read(void* /*buf*/, size_t /*size*/, ssize_t* /*nread*/)
{
    return srs_success;
}

MockBufferIO::MockBufferIO()
{
    rtm = stm = SRS_UTIME_NO_TIMEOUT;
    rbytes = sbytes = 0;
    in_err = out_err = srs_success;
}

MockBufferIO::~MockBufferIO()
{
}

int MockBufferIO::length()
{
    return in_buffer.length();
}

MockBufferIO* MockBufferIO::append(string data)
{
    in_buffer.append((char*)data.data(), data.length());
    return this;
}

MockBufferIO* MockBufferIO::append(MockBufferIO* data)
{
    in_buffer.append(&data->in_buffer);
    return this;
}

MockBufferIO* MockBufferIO::append(uint8_t* data, int size)
{
    in_buffer.append((char*)data, size);
    return this;
}

int MockBufferIO::out_length()
{
    return out_buffer.length();
}

MockBufferIO* MockBufferIO::out_append(string data)
{
    out_buffer.append((char*)data.data(), data.length());
    return this;
}

MockBufferIO* MockBufferIO::out_append(MockBufferIO* data)
{
    out_buffer.append(&data->out_buffer);
    return this;
}

MockBufferIO* MockBufferIO::out_append(uint8_t* data, int size)
{
    out_buffer.append((char*)data, size);
    return this;
}

srs_error_t MockBufferIO::read_fully(void* buf, size_t size, ssize_t* nread)
{
    if (in_err != srs_success) {
        return srs_error_copy(in_err);
    }

    if (in_buffer.length() < (int)size) {
        return srs_error_new(ERROR_SOCKET_READ, "read");
    }
    memcpy(buf, in_buffer.bytes(), size);
    
    rbytes += size;
    if (nread) {
        *nread = size;
    }
    in_buffer.erase(size);
    return srs_success;
}

srs_error_t MockBufferIO::write(void* buf, size_t size, ssize_t* nwrite)
{
    if (out_err != srs_success) {
        return srs_error_copy(out_err);
    }

    sbytes += size;
    if (nwrite) {
        *nwrite = size;
    }
    out_buffer.append((char*)buf, size);
    return srs_success;
}

void MockBufferIO::set_recv_timeout(srs_utime_t tm)
{
    rtm = tm;
}

srs_utime_t MockBufferIO::get_recv_timeout()
{
    return rtm;
}

int64_t MockBufferIO::get_recv_bytes()
{
    return rbytes;
}

void MockBufferIO::set_send_timeout(srs_utime_t tm)
{
    stm = tm;
}

srs_utime_t MockBufferIO::get_send_timeout()
{
    return stm;
}

int64_t MockBufferIO::get_send_bytes()
{
    return sbytes;
}

srs_error_t MockBufferIO::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
{
    srs_error_t err = srs_success;

    if (out_err != srs_success) {
        return srs_error_copy(out_err);
    }
    
    ssize_t total = 0;
    for (int i = 0; i <iov_size; i++) {
        const iovec& pi = iov[i];
        
        ssize_t writen = 0;
        if ((err = write(pi.iov_base, pi.iov_len, &writen)) != srs_success) {
            return err;
        }
        total += writen;
    }
    
    if (nwrite) {
        *nwrite = total;
    }
    return err;
}

srs_error_t MockBufferIO::read(void* buf, size_t size, ssize_t* nread)
{
    if (in_err != srs_success) {
        return srs_error_copy(in_err);
    }

    if (in_buffer.length() <= 0) {
        return srs_error_new(ERROR_SOCKET_READ, "read");
    }
    
    size_t available = srs_min(in_buffer.length(), (int)size);
    memcpy(buf, in_buffer.bytes(), available);
    
    rbytes += available;
    if (nread) {
        *nread = available;
    }
    in_buffer.erase(available);

    return srs_success;
}

MockStatistic::MockStatistic()
{
    in = out = 0;
}

MockStatistic::~MockStatistic()
{
}

int64_t MockStatistic::get_recv_bytes()
{
    return in;
}

int64_t MockStatistic::get_send_bytes()
{
    return out;
}

MockStatistic* MockStatistic::set_in(int64_t v)
{
    in = v;
    return this;
}

MockStatistic* MockStatistic::set_out(int64_t v)
{
    out = v;
    return this;
}

MockStatistic* MockStatistic::add_in(int64_t v)
{
    in += v;
    return this;
}

MockStatistic* MockStatistic::add_out(int64_t v)
{
    out += v;
    return this;
}

MockWallClock::MockWallClock()
{
    clock = 0;
}

MockWallClock::~MockWallClock()
{
}

srs_utime_t MockWallClock::now()
{
    return clock;
}

MockWallClock* MockWallClock::set_clock(srs_utime_t v)
{
    clock = v;
    return this;
}

// verify the sha256
VOID TEST(ProtocolHandshakeTest, OpensslSha256)
{
    // randome bytes to ensure the openssl sha256 is ok.
    uint8_t random_bytes[] = {
        0x8b, 0x1c, 0x5c, 0x5c, 0x3b, 0x98, 0x60, 0x80, 0x3c, 0x97, 0x43, 0x79, 0x9c, 0x94, 0xec, 0x63, 0xaa, 0xd9, 0x10, 0xd7, 0x0d, 0x91, 0xfb, 0x1f, 0xbf, 0xe0, 0x29, 0xde, 0x77, 0x09, 0x21, 0x34, 0xa5, 0x7d, 0xdf, 0xe3, 0xdf, 0x11, 0xdf, 0xd4, 0x00, 0x57, 0x38, 0x5b, 0xae, 0x9e, 0x89, 0x35, 0xcf, 0x07, 0x48, 0xca, 0xc8, 0x25, 0x46, 0x3c,
        0xb6, 0xdb, 0x9b, 0x39, 0xa6, 0x07, 0x3d, 0xaf, 0x8b, 0x85, 0xa2, 0x2f, 0x03, 0x64, 0x5e, 0xbd, 0xb4, 0x20, 0x01, 0x48, 0x2e, 0xc2, 0xe6, 0xcc, 0xce, 0x61, 0x59, 0x47, 0xf9, 0xdd, 0xc2, 0xa2, 0xfe, 0x64, 0xe6, 0x0b, 0x41, 0x4f, 0xe4, 0x8a, 0xca, 0xbe, 0x4d, 0x0e, 0x73, 0xba, 0x82, 0x30, 0x3c, 0x53, 0x36, 0x2e, 0xd3, 0x04, 0xae, 0x49,
        0x44, 0x71, 0x6d, 0x4d, 0x5a, 0x14, 0x94, 0x94, 0x57, 0x78, 0xb9, 0x2a, 0x34, 0x49, 0xf8, 0xc2, 0xec, 0x4e, 0x29, 0xb6, 0x28, 0x54, 0x4a, 0x5e, 0x68, 0x06, 0xfe, 0xfc, 0xd5, 0x01, 0x35, 0x0c, 0x95, 0x6f, 0xe9, 0x77, 0x8a, 0xfc, 0x11, 0x15, 0x1a, 0xda, 0x6c, 0xf5, 0xba, 0x9e, 0x41, 0xd9, 0x7e, 0x0f, 0xdb, 0x33, 0xda, 0x35, 0x9d, 0x34,
        0x67, 0x8f, 0xdf, 0x71, 0x63, 0x04, 0x9c, 0x54, 0xb6, 0x18, 0x10, 0x2d, 0x42, 0xd2, 0xf3, 0x14, 0x34, 0xa1, 0x31, 0x90, 0x48, 0xc9, 0x4b, 0x87, 0xb5, 0xcd, 0x62, 0x6b, 0x77, 0x18, 0x36, 0xd9, 0xc9, 0xc9, 0xae, 0x89, 0xfb, 0xed, 0xcd, 0xcb, 0xdb, 0x6e, 0xe3, 0x22, 0xbf, 0x7b, 0x72, 0x8a, 0xc3, 0x79, 0xd6, 0x1b, 0x6c, 0xe7, 0x9c, 0xc9,
        0xfd, 0x48, 0xaa, 0xc1, 0xfa, 0xbf, 0x33, 0x87, 0x5c, 0x0d, 0xe5, 0x34, 0x24, 0x70, 0x14, 0x1e, 0x4a, 0x48, 0x07, 0x6e, 0xaf, 0xbf, 0xfe, 0x34, 0x1e, 0x1e, 0x19, 0xfc, 0xb5, 0x8a, 0x4f, 0x3c, 0xb4, 0xcf, 0xde, 0x24, 0x79, 0x65, 0x17, 0x22, 0x3f, 0xc0, 0x06, 0x76, 0x4e, 0x3c, 0xfb, 0xc3, 0xd0, 0x7f, 0x7b, 0x87, 0x5c, 0xeb, 0x97, 0x87,
    };
    
    char digest[SRS_OpensslHashSize];
    ASSERT_EQ(ERROR_SUCCESS, 
        openssl_HMACsha256(
            SrsGenuineFPKey, 30, 
            (char*)random_bytes, sizeof(random_bytes),
            digest
        )
    );
    
    uint8_t expect_digest[] = {
        0x1b, 0xc7, 0xe6, 0x14, 0xd5, 0x19, 0x8d, 0x99, 0x42, 0x0a, 0x21, 0x95, 0x26, 0x9a, 0x8a, 0x56,
        0xb4, 0x82, 0x2a, 0x7f, 0xd3, 0x1d, 0xc3, 0xd8, 0x92, 0x97, 0xc4, 0x61, 0xb7, 0x4d, 0x5d, 0xd2
    };
    EXPECT_TRUE(srs_bytes_equals(digest, (char*)expect_digest, 32));
}

// verify the dh key
VOID TEST(ProtocolHandshakeTest, DHKey)
{
    srs_internal::SrsDH dh;
    
    ASSERT_TRUE(ERROR_SUCCESS == dh.initialize(true));
    
    char pub_key1[128];
    int pkey_size = 128;
    EXPECT_TRUE(ERROR_SUCCESS == dh.copy_public_key(pub_key1, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    char pub_key2[128];
    EXPECT_TRUE(ERROR_SUCCESS == dh.copy_public_key(pub_key2, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    EXPECT_TRUE(srs_bytes_equals(pub_key1, pub_key2, 128));
    
    // another dh
    srs_internal::SrsDH dh0;
    
    ASSERT_TRUE(ERROR_SUCCESS == dh0.initialize(true));
    
    EXPECT_TRUE(ERROR_SUCCESS == dh0.copy_public_key(pub_key2, pkey_size));
    ASSERT_EQ(128, pkey_size);
    
    EXPECT_FALSE(srs_bytes_equals(pub_key1, pub_key2, 128));
}

// flash will sendout a c0c1 encrypt by ssl.
VOID TEST(ProtocolHandshakeTest, VerifyFPC0C1)
{
    uint8_t c0c1[] = {
        0x03, 0x00, 0x0f, 0x64, 0xd0, 0x80, 0x00, 0x07, 0x02, 0xe6, 0x42, 0xe5, 0x2b, 0xf1, 0x1d, 0x0f, 0x6c, 0xc8, 0x50, 0xf2, 0x06, 0xae, 0xd5, 0x4f, 0xdb, 0xfe, 0x79, 0xc2, 0xef, 0xf5, 0x01, 0x74, 0x4b, 0x5b, 0xe7, 0x37, 0xa3, 0xe0, 0xca, 0xe1, 0x97, 0x07, 0xdb, 0x54, 0x1d, 0x4c, 0x4b, 0xa3, 0xc3, 0x3e, 0xa9, 0xeb, 0xa9, 0x5b, 0x2f, 0x38, 0xa0, 0xa9, 0x98, 0x38, 0x80, 0x1b, 0xfb, 0xa7, 0x04, 0xff, 0xfd, 0x45, 0xfe, 0xfa, 0xc1, 0xe4, 0x1c, 0x77, 0x9a, 0x19, 0x39, 0x34, 0x10, 0x79, 0x12, 0xcf, 0x4e, 0xea, 0x34, 0x7d, 0x88, 0x47, 0xca, 0xf2, 0xb3, 0x09, 0x50, 0xbb, 0xe1, 0x20, 0x9b, 0x25, 0xb0, 0x3c, 0xbc, 0x46, 0x7a, 0x36, 0xb8, 0xc2, 0x4d, 0xd0, 0xf1, 0x20, 0x2a, 0xcc, 0x7a, 0x91, 0xab, 0x0b, 0xb6, 0xc7, 0x09, 0x0d, 0xf1, 0x34, 0x0c, 0x37, 0xbe, 0xad, 0x0e, 0xe3, 0x6b, 0x68, 0x0a, 0x7e, 0xd2, 0xd4, 0xc5, 0x3d, 0xdc, 0xac, 0x28, 0x8b, 0x88, 0xb5, 0x1e, 0xd8, 0x2b, 0x68, 0x72, 0x55, 0x64, 0xa2, 0xa5, 0x69, 0x0a, 0xdb, 0x26, 0xff, 0x63, 0x2d, 0xb8, 0xff, 0xb6, 0x33, 0xd3, 0x9d, 0x5c, 0x46, 0xd6, 0xbf, 0x8b, 0x1c, 0x5c, 0x5c, 0x3b, 0x98, 0x60, 0x80, 0x3c, 0x97, 0x43, 0x79, 0x9c, 0x94, 0xec, 0x63, 0xaa, 0xd9, 0x10, 0xd7, 0x0d, 0x91, 0xfb, 0x1f, 0xbf, 0xe0, 0x29, 0xde, 0x77, 0x09, 0x21, 0x34, 0xa5, 0x7d, 0xdf, 0xe3, 0xdf, 0x11, 0xdf, 0xd4, 0x00, 0x57, 0x38, 0x5b, 0xae, 0x9e, 0x89, 0x35, 0xcf, 0x07, 0x48, 0xca, 0xc8, 0x25, 0x46, 0x3c,
        0xb6, 0xdb, 0x9b, 0x39, 0xa6, 0x07, 0x3d, 0xaf, 0x8b, 0x85, 0xa2, 0x2f, 0x03, 0x64, 0x5e, 0xbd, 0xb4, 0x20, 0x01, 0x48, 0x2e, 0xc2, 0xe6, 0xcc, 0xce, 0x61, 0x59, 0x47, 0xf9, 0xdd, 0xc2, 0xa2, 0xfe, 0x64, 0xe6, 0x0b, 0x41, 0x4f, 0xe4, 0x8a, 0xca, 0xbe, 0x4d, 0x0e, 0x73, 0xba, 0x82, 0x30, 0x3c, 0x53, 0x36, 0x2e, 0xd3, 0x04, 0xae, 0x49, 0x44, 0x71, 0x6d, 0x4d, 0x5a, 0x14, 0x94, 0x94, 0x57, 0x78, 0xb9, 0x2a, 0x34, 0x49, 0xf8, 0xc2, 0xec, 0x4e, 0x29, 0xb6, 0x28, 0x54, 0x4a, 0x5e, 0x68, 0x06, 0xfe, 0xfc, 0xd5, 0x01, 0x35, 0x0c, 0x95, 0x6f, 0xe9, 0x77, 0x8a, 0xfc, 0x11, 0x15, 0x1a, 0xda, 0x6c, 0xf5, 0xba, 0x9e, 0x41, 0xd9, 0x7e, 0x0f, 0xdb, 0x33, 0xda, 0x35, 0x9d, 0x34, 0x67, 0x8f, 0xdf, 0x71, 0x63, 0x04, 0x9c, 0x54, 0xb6, 0x18, 0x10, 0x2d, 0x42, 0xd2, 0xf3, 0x14, 0x34, 0xa1, 0x31, 0x90, 0x48, 0xc9, 0x4b, 0x87, 0xb5, 0xcd, 0x62, 0x6b, 0x77, 0x18, 0x36, 0xd9, 0xc9, 0xc9, 0xae, 0x89, 0xfb, 0xed, 0xcd, 0xcb, 0xdb, 0x6e, 0xe3, 0x22, 0xbf, 0x7b, 0x72, 0x8a, 0xc3, 0x79, 0xd6, 0x1b, 0x6c, 0xe7, 0x9c, 0xc9, 0xfd, 0x48, 0xaa, 0xc1, 0xfa, 0xbf, 0x33, 0x87, 0x5c, 0x0d, 0xe5, 0x34, 0x24, 0x70, 0x14, 0x1e, 0x4a, 0x48, 0x07, 0x6e, 0xaf, 0xbf, 0xfe, 0x34, 0x1e, 0x1e, 0x19, 0xfc, 0xb5, 0x8a, 0x4f, 0x3c, 0xb4, 0xcf, 0xde, 0x24, 0x79, 0x65, 0x17, 0x22, 0x3f, 0xc0, 0x06, 0x76, 0x4e, 0x3c, 0xfb, 0xc3, 0xd0, 0x7f, 0x7b, 0x87, 0x5c, 0xeb, 0x97, 0x87,
        0x99, 0x20, 0x70, 0x7b, 0xf8, 0x97, 0x73, 0xdc, 0xb4, 0x94, 0x43, 0x27, 0x03, 0xbd, 0xb5, 0x91, 0xd9, 0x3e, 0x51, 0x1a, 0xd5, 0x60, 0x9c, 0x71, 0xd3, 0xc7, 0x1f, 0xd7, 0xef, 0x2f, 0xa1, 0xf7, 0xe6, 0xb1, 0x31, 0x9d, 0xec, 0xa3, 0xe1, 0x01, 0x57, 0xa8, 0x1c, 0x34, 0xf8, 0x82, 0xf5, 0x4d, 0xb8, 0x32, 0xe4, 0x4b, 0x90, 0x97, 0xcf, 0x8c, 0x2e, 0x89, 0xd0, 0xbc, 0xc0, 0xca, 0x45, 0x5e, 0x5c, 0x36, 0x47, 0x98, 0xa8, 0x57, 0xb5, 0x56, 0xc9, 0x11, 0xe4, 0x2f, 0xf0, 0x2b, 0x2c, 0xc1, 0x49, 0x1a, 0xfb, 0xdd, 0x89, 0x3f, 0x18, 0x98, 0x78, 0x13, 0x83, 0xf4, 0x30, 0xe2, 0x4e, 0x0e, 0xf4, 0x6c, 0xcb, 0xc6, 0xc7, 0x31, 0xe9, 0x78, 0x74, 0xfd, 0x53, 0x05, 0x4e, 0x7b, 0xd3, 0x9b, 0xeb, 0x15, 0xc0, 0x6f, 0xbf, 0xa4, 0x69, 0x7d, 0xd1, 0x53, 0x0f, 0x0b, 0xc1, 0x2b, 0xad, 0x00, 0x44, 0x10, 0xe2, 0x9f, 0xb9, 0xf3, 0x0c, 0x98, 0x53, 0xf0, 0x60, 0xcb, 0xee, 0x7e, 0x5c, 0x83, 0x4a, 0xde, 0xa0, 0x7a, 0xcf, 0x50, 0x2b, 0x84, 0x09, 0xff, 0x42, 0xe4, 0x80, 0x2a, 0x64, 0x20, 0x9b, 0xb9, 0xba, 0xd4, 0x54, 0xca, 0xd8, 0xdc, 0x0a, 0x4d, 0xdd, 0x84, 0x91, 0x5e, 0x16, 0x90, 0x1d, 0xdc, 0xe3, 0x95, 0x55, 0xac, 0xf2, 0x8c, 0x9a, 0xcc, 0xb2, 0x6d, 0x17, 0x01, 0xe4, 0x01, 0xc6, 0xba, 0xe4, 0xb8, 0xd5, 0xbd, 0x7b, 0x43, 0xc9, 0x69, 0x6b, 0x40, 0xf7, 0xdc, 0x65, 0xa4, 0xf7, 0xca, 0x1f, 0xd8, 0xe5, 0xba, 0x4c, 0xdf, 0xe4, 0x64, 0x9e, 0x7d, 0xbd, 0x54, 0x13, 0x13,
        0xc6, 0x0c, 0xb8, 0x1d, 0x31, 0x0a, 0x49, 0xe2, 0x43, 0xb6, 0x95, 0x5f, 0x05, 0x6e, 0x66, 0xf4, 0x21, 0xa8, 0x65, 0xce, 0xf8, 0x8e, 0xcc, 0x16, 0x1e, 0xbb, 0xd8, 0x0e, 0xcb, 0xd2, 0x48, 0x37, 0xaf, 0x4e, 0x67, 0x45, 0xf1, 0x79, 0x69, 0xd2, 0xee, 0xa4, 0xb5, 0x01, 0xbf, 0x57, 0x0f, 0x68, 0x37, 0xbe, 0x4e, 0xff, 0xc9, 0xb9, 0x92, 0x23, 0x06, 0x75, 0xa0, 0x42, 0xe4, 0x0a, 0x30, 0xf0, 0xaf, 0xb0, 0x54, 0x88, 0x7c, 0xc0, 0xc1, 0x0c, 0x6d, 0x01, 0x36, 0x63, 0xf3, 0x3d, 0xbc, 0x72, 0xf6, 0x96, 0xc8, 0x87, 0xab, 0x8b, 0x0c, 0x91, 0x2f, 0x42, 0x2a, 0x11, 0xf6, 0x2d, 0x5e, 0x77, 0xce, 0x9c, 0xc1, 0x34, 0xe5, 0x2d, 0x9b, 0xd0, 0x37, 0x97, 0x0e, 0x39, 0xe5, 0xaa, 0xbe, 0x15, 0x3e, 0x6b, 0x1e, 0x73, 0xf6, 0xd7, 0xf4, 0xd6, 0x71, 0x70, 0xc6, 0xa1, 0xe6, 0x04, 0xd3, 0x7c, 0x2d, 0x1c, 0x98, 0x47, 0xdb, 0x8f, 0x59, 0x99, 0x2a, 0x57, 0x63, 0x14, 0xc7, 0x02, 0x42, 0x74, 0x57, 0x02, 0x22, 0xb2, 0x55, 0xe9, 0xf3, 0xe0, 0x76, 0x1c, 0x50, 0xbf, 0x43, 0x65, 0xbe, 0x52, 0xbd, 0x46, 0xf0, 0xfd, 0x5e, 0x25, 0xfe, 0x34, 0x50, 0x0d, 0x24, 0x7c, 0xfc, 0xfa, 0x82, 0x2f, 0x8c, 0x7d, 0x97, 0x1b, 0x07, 0x6b, 0x20, 0x6c, 0x9b, 0x7b, 0xae, 0xbf, 0xb3, 0x4f, 0x6e, 0xbb, 0xb6, 0xc4, 0xe9, 0xa5, 0x07, 0xa7, 0x74, 0x45, 0x16, 0x8a, 0x12, 0xee, 0x42, 0xc8, 0xea, 0xb5, 0x33, 0x69, 0xef, 0xff, 0x60, 0x6d, 0x99, 0xa3, 0x92, 0x5d, 0x0f, 0xbe, 0xb7, 0x4e, 0x1c, 0x85,
        0xef, 0x9e, 0x1d, 0x38, 0x72, 0x1f, 0xe0, 0xca, 0xc9, 0x90, 0x85, 0x3f, 0xa6, 0x5d, 0x60, 0x3f, 0xe6, 0x92, 0x08, 0x3b, 0xd4, 0xc3, 0xa2, 0x7e, 0x7c, 0x35, 0x49, 0xd4, 0x21, 0x38, 0x8c, 0x2c, 0x49, 0xb3, 0xcb, 0x33, 0xd4, 0xc2, 0x88, 0xdc, 0x09, 0xb3, 0x8a, 0x13, 0x95, 0x0f, 0xb4, 0x0a, 0xd1, 0x1d, 0xc8, 0xe4, 0x64, 0xb4, 0x24, 0x51, 0xe1, 0x0a, 0x22, 0xd4, 0x45, 0x77, 0x91, 0x0a, 0xc6, 0x61, 0xa1, 0x2c, 0x50, 0x84, 0x1c, 0x0c, 0xbe, 0x05, 0x1c, 0x3b, 0x4f, 0x27, 0x83, 0x33, 0xba, 0xfb, 0x7f, 0xa0, 0xc6, 0x38, 0xb4, 0x0c, 0x15, 0x49, 0x8f, 0xfa, 0x17, 0x76, 0xa9, 0x54, 0xf4, 0x6c, 0x7e, 0x5e, 0x39, 0xb8, 0xa8, 0x78, 0x86, 0x48, 0xb2, 0x18, 0xf1, 0xde, 0x0d, 0x24, 0xee, 0x6b, 0x01, 0x7d, 0x60, 0xfa, 0x35, 0xfe, 0x71, 0x0b, 0xfa, 0x8c, 0x79, 0x6c, 0x0b, 0x25, 0x84, 0x6d, 0x1a, 0x1d, 0xe0, 0x33, 0xa1, 0xa0, 0x8f, 0x47, 0x08, 0x4b, 0x5c, 0x8c, 0xc6, 0x1e, 0x2a, 0x6d, 0xd8, 0x3e, 0x09, 0x83, 0x96, 0xe6, 0xbc, 0x14, 0x55, 0x17, 0xcb, 0x50, 0x44, 0xdb, 0x80, 0xab, 0xb9, 0xf0, 0x1a, 0x3a, 0x9e, 0x23, 0xd5, 0x46, 0x73, 0x4b, 0xd0, 0x41, 0x9d, 0x29, 0x03, 0x59, 0x29, 0xeb, 0x82, 0x71, 0x09, 0x0c, 0x26, 0x10, 0x0f, 0x59, 0xd4, 0xd7, 0xb4, 0x4d, 0xe5, 0x35, 0xf5, 0x19, 0xef, 0xc7, 0xe7, 0x43, 0x0a, 0x3e, 0xeb, 0x3d, 0xc5, 0x55, 0xde, 0x04, 0xe7, 0x88, 0x72, 0x6c, 0xf7, 0x9d, 0x86, 0xb2, 0x0c, 0x83, 0x55, 0x20, 0x67, 0xc0, 0xc9, 0x15,
        0x3c, 0x76, 0x69, 0x80, 0x79, 0x68, 0x89, 0x16, 0x0a, 0xaf, 0xe4, 0x2c, 0xf0, 0x0e, 0x26, 0x74, 0x84, 0xfb, 0x27, 0xd4, 0x1c, 0x61, 0xbe, 0xe8, 0xc3, 0xce, 0x74, 0xd9, 0xf8, 0x5a, 0xa8, 0x63, 0x13, 0x27, 0xfa, 0xab, 0x93, 0x32, 0x25, 0x18, 0xb1, 0x78, 0x2f, 0xd3, 0x93, 0x0b, 0xc6, 0x5a, 0xda, 0xfe, 0xff, 0x7e, 0x38, 0x0c, 0x26, 0x44, 0x4c, 0x23, 0xe0, 0x8e, 0x64, 0xff, 0x07, 0xbc, 0x5b, 0x87, 0xd6, 0x3c, 0x8e, 0xe7, 0xd1, 0x78, 0x55, 0x00, 0x19, 0xbe, 0x98, 0x55, 0x1e, 0x16, 0xea, 0x63, 0x79, 0xb5, 0xaf, 0x9a, 0x20, 0x04, 0x8d, 0x3f, 0xdc, 0x15, 0x29, 0xc4, 0xe3, 0x9a, 0x82, 0x92, 0x85, 0xee, 0x1c, 0x37, 0xb3, 0xd7, 0xd2, 0x2e, 0x1e, 0xdb, 0x59, 0x87, 0xef, 0xa8, 0x9a, 0xaa, 0xa4, 0xed, 0x89, 0x33, 0xa8, 0xa7, 0x6c, 0x96, 0x9f, 0x26, 0xeb, 0xdc, 0x61, 0xc4, 0x8f, 0xd3, 0x2b, 0x81, 0x86, 0x6c, 0x9c, 0xc2, 0xb1, 0xb5, 0xbc, 0xa6, 0xd6, 0xd6, 0x1d, 0xce, 0x93, 0x78, 0xb3, 0xec, 0xa8, 0x64, 0x19, 0x13, 0x59, 0x1c, 0xb9, 0xbf, 0xd8, 0x7f, 0x27, 0x8e, 0x6f, 0x05, 0xd9, 0x1a, 0xa4, 0x1a, 0xc2, 0x46, 0x81, 0x52, 0xa5, 0xaf, 0x73, 0x35, 0x34, 0x88, 0x60, 0x46, 0x4d, 0x09, 0x87, 0xf1, 0x7e, 0x5e, 0xea, 0x32, 0x98, 0xb4, 0x68, 0x28, 0xff, 0x47, 0xde, 0x72, 0x9b, 0xc5, 0xfe, 0xb8, 0x93, 0xe8, 0x79, 0xe4, 0xa6, 0xd7, 0x63, 0x94, 0x29, 0x94, 0x33, 0x30, 0x61, 0xd4, 0x19, 0x36, 0x99, 0x94, 0x31, 0xbf, 0x93, 0x46, 0x04, 0xc0, 0xfe, 0x4d,
        0x92, 0xb4, 0xbc, 0xb2, 0x14, 0x3f, 0xf7, 0xce, 0x05, 0xcf, 0xf2, 0x5b, 0x66, 0xcb, 0x67, 0xa9, 0x8f, 0x63, 0xd4, 0x7c, 0x1d, 0x33, 0x6a, 0x05, 0xfb, 0xf7, 0x11, 0x03, 0x97, 0xff, 0x02, 0x1b, 0x6f, 0x15, 0x8b, 0x33, 0xe6, 0xf7, 0x5d, 0x93, 0x21, 0x9d, 0x17, 0xde, 0x9e, 0x87, 0xdc, 0xcd, 0x9a, 0x6a, 0x30, 0x3e, 0xa9, 0x70, 0xed, 0x93, 0x1d, 0x43, 0xb5, 0x5d, 0xb0, 0x46, 0x74, 0x73, 0x3b, 0x25, 0xfa, 0x0e, 0xe3, 0x70, 0x74, 0x2d, 0x75, 0xd6, 0x14, 0x67, 0x40, 0x31, 0xf9, 0x2c, 0xf6, 0x38, 0xea, 0x45, 0x33, 0xc1, 0xb6, 0xd5, 0x93, 0x0f, 0x5c, 0xaf, 0x3a, 0x53, 0x75, 0xd6, 0xe8, 0x97, 0xa0, 0x51, 0x3f, 0x96, 0x41, 0x32, 0x0b, 0x59, 0x48, 0xbf, 0x2b, 0x19, 0x67, 0x98, 0x42, 0xfe, 0x44, 0x23, 0x84, 0xa9, 0x09, 0x40, 0x4e, 0x10, 0x25, 0xdf, 0x68, 0x93, 0x6b, 0x0d, 0xa8, 0x51, 0x47, 0x55, 0xb7, 0xb8, 0x22, 0xab, 0xa3, 0x3c, 0x78, 0xd6, 0x8b, 0x4f, 0x2a, 0x73, 0xc1, 0x4a, 0x4a, 0xdd, 0x73, 0xb1, 0xc0, 0x8c, 0x5f, 0xf6, 0xe7, 0xbe, 0x9c, 0x96, 0xd6, 0x37, 0x91, 0x05, 0x52, 0xd1, 0x2f, 0xa9, 0xdc, 0xca, 0x11, 0x30, 0x6d, 0x4f, 0xb5, 0x6e, 0x39, 0x24, 0x28, 0x80, 0x54, 0x28, 0x87, 0xe6, 0x40, 0xeb, 0xd8, 0x7a, 0x1f, 0x63, 0x56, 0xc1, 0x4d, 0xa0, 0xf8
    };
    ASSERT_EQ(1537, (int)sizeof(c0c1));
    
    // c0
    EXPECT_EQ(0x03, c0c1[0]);
    
    // c1
    c1s1 c1;
    
    // the schema of data must be schema0: key-digest.
    ASSERT_EQ(ERROR_SUCCESS, c1.parse((char*)c0c1 + 1, 1536, srs_schema0));
    EXPECT_EQ((int32_t)0x000f64d0, c1.time);
    EXPECT_EQ((int32_t)0x80000702, c1.version);
    
    // manually validate the c1
    // @see: calc_c1_digest
    char* c1s1_joined_bytes = new char[1536 -32];
    SrsAutoFree(char, c1s1_joined_bytes);
    ASSERT_EQ(ERROR_SUCCESS, c1.payload->copy_to(&c1, c1s1_joined_bytes, 1536 - 32, false));
    
    bool is_valid;
    ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid));
    ASSERT_TRUE(is_valid);
    
    // 128bytes key
    uint8_t key[] = {
        0x01, 0xc6, 0xba, 0xe4, 0xb8, 0xd5, 0xbd, 0x7b, 0x43, 0xc9, 0x69, 0x6b, 0x40, 0xf7, 0xdc, 0x65, 0xa4, 0xf7, 0xca, 0x1f, 0xd8, 0xe5, 0xba, 0x4c, 0xdf, 0xe4, 0x64, 0x9e, 0x7d, 0xbd, 0x54, 0x13, 0x13, 0xc6, 0x0c, 0xb8, 0x1d, 0x31, 0x0a, 0x49, 0xe2, 0x43, 0xb6, 0x95, 0x5f, 0x05, 0x6e, 0x66,
        0xf4, 0x21, 0xa8, 0x65, 0xce, 0xf8, 0x8e, 0xcc, 0x16, 0x1e, 0xbb, 0xd8, 0x0e, 0xcb, 0xd2, 0x48, 0x37, 0xaf, 0x4e, 0x67, 0x45, 0xf1, 0x79, 0x69, 0xd2, 0xee, 0xa4, 0xb5, 0x01, 0xbf, 0x57, 0x0f, 0x68, 0x37, 0xbe, 0x4e, 0xff, 0xc9, 0xb9, 0x92, 0x23, 0x06, 0x75, 0xa0, 0x42, 0xe4, 0x0a, 0x30,
        0xf0, 0xaf, 0xb0, 0x54, 0x88, 0x7c, 0xc0, 0xc1, 0x0c, 0x6d, 0x01, 0x36, 0x63, 0xf3, 0x3d, 0xbc, 0x72, 0xf6, 0x96, 0xc8, 0x87, 0xab, 0x8b, 0x0c, 0x91, 0x2f, 0x42, 0x2a, 0x11, 0xf6, 0x2d, 0x5e
    };
    EXPECT_TRUE(srs_bytes_equals(c1.get_key(), (char*)key, 128));
    
    // 32bytes digest
    uint8_t digest[] = {
        0x6c, 0x96, 0x9f, 0x26, 0xeb, 0xdc, 0x61, 0xc4, 0x8f, 0xd3, 0x2b, 0x81, 0x86, 0x6c, 0x9c, 0xc2,
        0xb1, 0xb5, 0xbc, 0xa6, 0xd6, 0xd6, 0x1d, 0xce, 0x93, 0x78, 0xb3, 0xec, 0xa8, 0x64, 0x19, 0x13
    };
    EXPECT_TRUE(srs_bytes_equals(c1.get_digest(), (char*)digest, 32));
}

VOID TEST(ProtocolHandshakeTest, ComplexHandshake)
{
    srs_error_t err;

    uint8_t c0c1[] = {
        0x03, 0x01, 0x14, 0xf7, 0x4e, 0x80, 0x00, 0x07, 0x02, 0xac, 0x14, 0x98, 0x57, 0x0a, 0x07, 0x58, 0x44, 0x96, 0x47, 0xb5, 0x9a, 0x73, 0xf6, 0x07, 0x0f, 0x49, 0x0d, 0x72, 0xb8, 0x16, 0xbb, 0xb2, 0xb7, 0x61, 0x17, 0x79, 0xa0, 0xe9, 0x98, 0xca, 0xb2, 0x86, 0x64, 0x5f, 0x65, 0x3e, 0xfc, 0x4d, 0xc0, 0x0e, 0x4c, 0xfa, 0x91, 0xc7, 0x0f, 0x2e, 0x57, 0x31, 0x4b, 0x96, 0xef, 0xc9, 0x81, 0x02, 0x00, 0x54, 0x25, 0x2b, 0xb2, 0x0d, 0x7c, 0xee, 0xba, 0xdb, 0xe4, 0x06, 0x78, 0xcd, 0x70, 0x2c, 0x54, 0x5a, 0x3a, 0x03, 0x13, 0x2e, 0xe7, 0x4b, 0x87, 0x40, 0x77, 0x0b, 0x9f, 0xd2, 0xab, 0x32, 0x07, 0x6f, 0x1e, 0x75, 0x74, 0xe9, 0xc7, 0x44, 0xd9, 0x76, 0x53, 0xba, 0xe2, 0x52, 0xfa, 0xcc, 0xef, 0x34, 0xd5, 0x14, 0x61, 0xac, 0xcc, 0x63, 0xfd, 0x2b, 0x2d, 0xb3, 0xb8, 0xdd, 0x8a, 0x51, 0x9a, 0x2d, 0x0e, 0xfa, 0x84, 0x25, 0x55, 0xb2, 0xb7, 0x94, 0x54, 0x68, 0xfb, 0x94, 0xdf, 0xd8, 0xeb, 0x43, 0xd0, 0x11, 0x70, 0x8f, 0xf5, 0x48, 0xfc, 0x69, 0x4d, 0x5b, 0xc6, 0x53, 0x8a, 0x22, 0xea, 0x62, 0x84, 0x89, 0x6b, 0xfe, 0x4e, 0xab, 0x51, 0x98, 0xf4, 0x4f, 0xae, 0xf8, 0xdf, 0xac, 0x43, 0xed, 0x5a, 0x04, 0x97, 0xc4, 0xbe, 0x44, 0x5b, 0x99, 0x20, 0x68, 0x67, 0x0f, 0xe3, 0xfa, 0x4c, 0x9d, 0xe7, 0x0b, 0x3f, 0x80, 0x7c, 0x4c, 0x35, 0xf6, 0xdd, 0x20, 0x05, 0xfd, 0x0f, 0x39, 0xb7, 0x36, 0x45, 0x4c, 0xb7, 0x62, 0x92, 0x35, 0x2a, 0xcd, 0xb9, 0x49, 0xea, 0x12, 0x0b, 0x5f, 0x39, 0xae, 0x3b, 0x49, 0x29, 0xe6, 0x30, 0xc7, 0x7c, 0x77, 0xaf, 0x00, 0x43, 0x4d, 0x06, 0x45, 0x72, 0x73, 0x25, 0x71, 0x5e, 0x35, 0x04, 0xbd, 0xe9, 0x48, 0x23, 0x64, 0x4d, 0x15, 0x0b, 0xc5, 0x3f, 0x6e, 0x3a, 0xd5, 0xd5, 0xa6, 0xae, 0x3b, 0x4c, 0x66, 0x6a, 0x70, 0x8b, 0xf3, 0x6a, 0x43, 0xc4, 0xb9, 0xbd, 0xa0, 0x09, 0x72, 0xbc, 0xce, 0x7a, 0xea, 0x49, 0xf2, 0x86, 0xa7, 0xd8, 0x4a, 0x87, 0x28, 0xca, 0x2c, 0x53, 0xee, 0x96, 0x0b, 0xbe, 0x15, 0x14, 0xa8, 0x00, 0xca, 0x76, 0x08, 0x4d, 0x0f, 0xef, 0x78, 0x4b, 0xf6, 0x47, 0x60, 0xfc, 0x16, 0x00, 0x7c, 0x6b, 0x49, 0x39, 0x64, 0x36, 0xee, 0x45, 0x3a, 0x9a, 0xa5, 0xbf, 0xfb, 0x7b, 0xe7, 0xcf, 0x42, 0x82, 0x48, 0x1b, 0x30, 0xfe, 0x0d, 0xba, 0x10, 0xb8, 0xe1, 0x40, 0xcc, 0x6f, 0x36, 0x1c, 0x94, 0x5d, 0x50, 0x9e, 0x21, 0x08, 0xc9, 0xd5, 0xb0, 0x32, 0x51, 0x6a, 0x8f, 0xfa, 0x57, 0x8d, 0x45, 0xd7, 0xd2, 0xd0, 0xd6, 0x6c, 0x78, 0x95, 0xe9, 0xe1, 0x20, 0x97, 0x1a, 0x43, 0x40, 0xa3, 0xb5, 0xcc, 0x4b, 0x12, 0x84, 0x1e, 0x0e, 0xd3, 0x32, 0xca, 0x99, 0xc3, 0x2b, 0x78, 0x17, 0x24, 0x6b, 0xc7, 0xbc, 0x9d, 0x05, 0xc6, 0xaf, 0x8f, 0x19, 0x75, 0x3c, 0x08, 0xa6, 0x08, 0x26, 0x5b, 0xf4, 0x10, 0x40, 0xaa, 0x6a, 0x7e, 0xb9, 0xde, 0x0b, 0x23, 0x3f, 0x53, 0x5a, 0x20, 0x13, 0x62, 0xec, 0x53, 0x86, 0x81, 0x1f, 0xf6, 0x8e, 0xe3, 0xd1, 0xaa, 0xb5, 0x41, 0x87, 0x62, 0xd2, 0xb7, 0x09, 0x12, 0x71, 0x01, 0x2c, 0xac, 0x6d, 0x9d, 0x37, 0x46, 0x5b, 0xdc, 0x76, 0x2c, 0x96, 0x61, 0x88, 0x55, 0x5a, 0x20, 0xc2, 0x84, 0x95, 0xbd, 0x72, 0xc4, 0xb7, 0x22, 0xae, 0xeb, 0x49, 0x0e, 0x16, 0xf1, 0xf1, 0xbf, 0xc5, 0xc7, 0xa8, 0x8d, 0xfb, 0xe1, 0x08, 0x6c, 0xc4, 0x79, 0x81, 0x13, 0xe8, 0x39, 0xbf, 0x6e, 0x5c, 0xa1, 0x62, 0xfb, 0x32, 0x2a, 0x62, 0xf0, 0x12, 0x07, 0x31, 0x93, 0x40, 0xf3, 0xc0, 0xea, 0x1d, 0xd8, 0x65, 0xba, 0x12, 0xb3, 0x9b, 0xf5, 0x59, 0x9c, 0x4e, 0xf6, 0xb9, 0xf7, 0x85, 0xa1, 0xd9, 0x2f, 0x7c, 0x8b, 0xd0, 0xfc, 0x53, 0x3b, 0xed, 0x85, 0xa4, 0xd2, 0x5e, 0x69, 0x61, 0x02, 0x53, 0xb6, 0x19, 0xc7, 0x82, 0xea, 0x8a, 0x45, 0x01, 0x5d, 0x4b, 0xb3, 0x06, 0x86, 0x7f, 0x4b, 0x2f, 0xe7, 0xa8, 0xd0, 0x28, 0x62, 0x02, 0xe8, 0xf3, 0x9e, 0x1e, 0x72, 0x82, 0x07, 0x9f, 0xdd, 0xd2, 0x83, 0x7d, 0x89, 0x73, 0x1b, 0x6f, 0x35, 0x20, 0xb7, 0x88, 0x15, 0x92, 0xa7, 0x11, 0xfe, 0x81, 0x68, 0xed, 0x14, 0x07, 0xdf, 0x4a, 0x06, 0x9c, 0x5e, 0x7e, 0x34, 0x3a, 0x2a, 0x8a, 0xd3, 0xe8, 0xf8, 0xd4, 0xdb, 0xe3, 0xe9, 0x73, 0xbf, 0xa7, 0xe9, 0x73, 0x62, 0xf2, 0x9d, 0xc1, 0xf7, 0x51, 0xeb, 0xff, 0xb7, 0xe6, 0xd9, 0xac, 0x46, 0x06, 0x74, 0xe2, 0x25, 0x3f, 0x46, 0x43, 0xce, 0x49, 0x52, 0x25, 0x1b, 0xf9, 0x24, 0x5c, 0xda, 0xfd, 0x7f, 0xf6, 0xef, 0xb3, 0xd5, 0xe9, 0x6e, 0x35, 0xb8, 0xd1, 0x0e, 0x2c, 0xc1, 0x48, 0x5a, 0x27, 0x0a, 0x81, 0x01, 0x0f, 0xe4, 0x51, 0xcf, 0x89, 0x36, 0xd3, 0xe8, 0x5e, 0x05, 0xb9, 0x83, 0x42, 0xf3, 0xa5, 0x94, 0x67, 0x6d, 0x6a, 0x6e, 0xad, 0xf8, 0x90, 0xb1, 0x1d, 0x63, 0x18, 0x52, 0xc1, 0xbf, 0xbc, 0xad, 0xf4, 0xd2, 0xc5, 0xef, 0xca, 0x4c, 0xfe, 0xa1, 0xda, 0x15, 0x92, 0x4c, 0x42, 0x3d, 0xfc, 0x80, 0x7e, 0x49, 0x13, 0x4e, 0xf6, 0xe1, 0xee, 0x70, 0xca, 0xd9, 0x0a, 0xde, 0x9b, 0xea, 0xcd, 0xf9, 0x90, 0xfd, 0xae, 0x09, 0xce, 0xb6, 0xa0, 0xf7, 0xd1, 0xe6, 0x0c, 0x55, 0x1e, 0x3f, 0xbb, 0x1e, 0xff, 0x3d, 0xdb, 0xdd, 0x27, 0x80, 0x06, 0x53, 0x7e, 0x0b, 0x2a, 0x80, 0x24, 0x51, 0x5c, 0x6a, 0xab, 0x32, 0x5d, 0x37, 0x8a, 0xf4, 0xb7, 0x11, 0xa7, 0xc1, 0x9e, 0x05, 0x2c, 0x16, 0xc2, 0x08, 0xe2, 0xac, 0x1a, 0xeb, 0x60, 0xf8, 0xd2, 0xea, 0x39, 0x01, 0x1c, 0x64, 0xbd, 0x22, 0x80, 0x19, 0x20, 0xc9, 0x6f, 0xdd, 0x5c, 0x73, 0x8c, 0xa1, 0x53, 0x48, 0x2e, 0x99, 0x1d, 0xc0, 0x8f, 0x28, 0xf1, 0xe3, 0xc5, 0xc5, 0x65, 0x53, 0xf2, 0x44, 0x44, 0x24, 0xb9, 0xe2, 0x73, 0xe4, 0x76, 0x14, 0x56, 0xb8, 0x82, 0xe3, 0xb4, 0xfd, 0x68, 0x31, 0xed, 0x40, 0x10, 0x99, 0xd3, 0x3d, 0xe5, 0x6b, 0x14, 0x61, 0x66, 0x9a, 0xf6, 0x33, 0x98, 0xc5, 0x4d, 0x11, 0xbb, 0xf8, 0x56, 0xf8, 0x8f, 0xd7, 0xb9, 0xda, 0xa3, 0x56, 0x1a, 0xe0, 0x9e, 0xbe, 0x5f, 0x56, 0xe5, 0xb9, 0xd8, 0xf3, 0xbc, 0x19, 0xf5, 0xe9, 0x1f, 0xd2, 0xea, 0xf4, 0x5a, 0xde, 0xed, 0xd4, 0x9e, 0xc8, 0xf5, 0x54,
        0x83, 0x8b, 0x8c, 0x2d, 0x24, 0x0e, 0x30, 0xb1, 0x84, 0xa2, 0xbe, 0x2c, 0x86, 0xe6, 0x42, 0x82, 0xaa, 0x37, 0x64, 0x55, 0x51, 0xbc, 0xde, 0xc0, 0x63, 0x88, 0xf6, 0x31, 0x71, 0x52, 0xd5, 0x34, 0x0f, 0x8e, 0xcb, 0x28, 0x65, 0x93, 0x1a, 0x66, 0x3b, 0x21, 0x00, 0xaa, 0x7a, 0xda, 0x2d, 0xf6, 0x7e, 0xb5, 0x27, 0x79, 0xf4, 0x50, 0x3b, 0x10, 0x6b, 0x3c, 0xd7, 0x99, 0x9d, 0xf6, 0xc5, 0x01, 0x91, 0xa0, 0xd5, 0x4f, 0xd3, 0x76, 0x54, 0xa8, 0x5c, 0x35, 0x1d, 0xe2, 0x35, 0x6a, 0x68, 0x67, 0x03, 0xc4, 0x1f, 0xe9, 0x60, 0xb8, 0x49, 0xb1, 0x9a, 0x40, 0xd9, 0x3c, 0x4c, 0x73, 0xaa, 0x88, 0x63, 0xaf, 0xfe, 0xe8, 0xa8, 0x0c, 0x96, 0xbe, 0xb4, 0x65, 0x7c, 0x27, 0xfb, 0xc1, 0x27, 0x24, 0x58, 0xab, 0x4b, 0xa0, 0x5a, 0x7d, 0xc7, 0xca, 0x2d, 0xa5, 0x22, 0xa7, 0xed, 0x26, 0x87, 0xd5, 0x44, 0x1a, 0xc7, 0xdd, 0xfb, 0x60, 0xfc, 0xe5, 0x50, 0xd9, 0x8d, 0xa7, 0xdb, 0x78, 0xb6, 0x9d, 0x80, 0x0f, 0xb9, 0x5f, 0xa7, 0x53, 0x92, 0x5d, 0x18, 0xce, 0x89, 0xc2, 0x69, 0xee, 0xcf, 0xb6, 0x66, 0xe5, 0x66, 0xd2, 0xe3, 0x35, 0x74, 0x0b, 0x83, 0xb6, 0xde, 0xf1, 0xfb, 0xb4, 0x1d, 0x4b, 0x94, 0x95, 0x06, 0x82, 0xe7, 0x1c, 0xf8, 0xc5, 0xe6, 0xd0, 0xf2, 0x17, 0x37, 0x44, 0xfe, 0x99, 0x43, 0x82, 0xbb, 0x88, 0xe4, 0x43, 0x67, 0xcc, 0x4d, 0x5f, 0xa6, 0x26, 0xd7, 0x53, 0xd6, 0x45, 0x96, 0x2b, 0x63, 0xd1, 0x2a, 0xa1, 0x2c, 0x41, 0x59, 0x8b, 0xb8, 0xc1, 0x89, 0x03, 0x3a, 0x61, 0x13, 0xc4, 0x2c, 0x37, 0xa5, 0xbf, 0xd7, 0xdb, 0xd8, 0x53, 0x5f, 0xa1, 0xdb, 0xdb, 0xa5, 0x73, 0xb6, 0xf7, 0x74, 0xa0, 0xf8, 0x93, 0xf5, 0x61, 0xee, 0x3c, 0xe7, 0x00, 0x01, 0x98, 0xe0, 0xa1, 0x22, 0xb6, 0x9a, 0x83, 0x44, 0xa1, 0xe6, 0x70, 0x56, 0x65, 0x92, 0x1e, 0xf0, 0xbc, 0x73, 0xa5, 0x7a, 0xc1, 0x1a, 0x02, 0xf9, 0xd4, 0xc4, 0x7c, 0x81, 0xda, 0x15, 0xc0, 0xd4, 0x25, 0xdc, 0x17, 0xa6, 0x0d, 0x90, 0x55, 0xf2, 0x10, 0xf8, 0xa7, 0x71, 0x9b, 0xed, 0xdf, 0xdf, 0xa1, 0xe4, 0xb9, 0x12, 0x6b, 0x05, 0x3e, 0x83, 0x99, 0x49, 0xbf, 0x66, 0xbb, 0xf6, 0x76, 0xd3, 0xa9, 0x24, 0x61, 0x8c, 0x25, 0x49, 0xd0, 0xf7, 0x83, 0x44, 0xfb, 0x27, 0xe2, 0x7d, 0x69, 0x6d, 0x34, 0x67, 0xed, 0x39, 0x89, 0x02, 0xcb, 0x2f, 0x33, 0x3c, 0xcd, 0x12, 0x42, 0x8f, 0x86, 0x7d, 0xda, 0x3f, 0xd7, 0x26, 0x62, 0x9c, 0x1f, 0x2e, 0xa8, 0xc3, 0x85, 0xf1, 0x73, 0xe5, 0x2c, 0x11, 0xde, 0x98, 0xc8, 0xb0, 0x10, 0x17, 0x55, 0xf5, 0x32, 0x52, 0x67, 0xca, 0x64, 0x50, 0x28, 0x9a, 0x24, 0x92, 0xa1, 0x97, 0x57, 0x81, 0xaf, 0xca, 0x1e, 0xc0, 0xa4, 0x71, 0x2d, 0x2a, 0xec, 0xc9, 0x23, 0x6a, 0x0c, 0x1d, 0x54, 0x15, 0x2a, 0x56, 0x42, 0x0a, 0x83, 0xff, 0x28, 0xba, 0xe7, 0x68, 0x38, 0xf5, 0x32, 0xa9, 0xb7, 0xe7, 0x70, 0x32, 0xa8, 0x79, 0x5e, 0x46, 0x1d, 0xec, 0x29, 0x8a, 0xde, 0x41, 0x94, 0x94, 0x26, 0x79, 0xc2, 0x52, 0x23, 0xe0, 0xa1, 0x1d, 0x65, 0x0c, 0xbe, 0x1b, 0x87, 0x2a, 0x21, 0x53, 0x2f, 0x35, 0x56, 0xe8, 0xd1, 0x7b, 0xb8, 0x23, 0x75, 0x56, 0xc7, 0x08, 0x9d, 0x13, 0xf0, 0x8f, 0x80, 0x38, 0xe9, 0x92, 0xf7, 0x16, 0xc2, 0xf3, 0x74, 0xa7, 0x92, 0xf5, 0x49, 0x7d, 0x09, 0x41, 0xbc, 0x07, 0x61, 0x1f, 0xe6, 0xa0, 0xd8, 0xa6, 0xe3, 0x72, 0xa4, 0x59, 0x4a, 0xd9, 0x33, 0x40, 0x80, 0x3a, 0x3a, 0xb3, 0xa0, 0x96, 0xca, 0x56, 0x98, 0xbd, 0x1f, 0x80, 0x86, 0x6c, 0xe1, 0x09, 0x64, 0x1b, 0x1a, 0xc9, 0x52, 0xaa, 0xd1, 0x39, 0xea, 0x4b, 0x6a, 0x3e, 0x4e, 0xa4, 0xea, 0x00, 0xde, 0x07, 0x0b, 0x23, 0xbc, 0x40, 0xc4, 0xd2, 0xd9, 0xf6, 0xda, 0x8e, 0x22, 0x36, 0xbe, 0x5e, 0x65, 0x6e, 0xbe, 0xc8, 0xb0, 0x07, 0xa2, 0x2d, 0xe9, 0x4b, 0x73, 0x54, 0xe6, 0x0a, 0xf2, 0xd3, 0x83, 0x8b, 0x27, 0x4c, 0xcc, 0x0c, 0x8a, 0xd4, 0x2b, 0xb8, 0x95, 0x2e, 0x42, 0x64, 0x29, 0xc1, 0xe0, 0x6b, 0x92, 0xab, 0xfe, 0x53, 0x06, 0x96, 0x4a, 0x8c, 0x5d, 0x7c, 0x51, 0x74, 0xd0, 0x1e, 0x37, 0x35, 0x9c, 0x1e, 0x69, 0x8f, 0x68, 0x18, 0xd9, 0xbe, 0xaf, 0x81, 0x9b, 0x7e, 0xd8, 0x71, 0x9d, 0xb6, 0x50, 0x43, 0x78, 0x85, 0x7d, 0x65, 0x93, 0x45, 0xb4, 0x02, 0xd0, 0x5c, 0x36, 0xe2, 0x62, 0x3f, 0x40, 0x33, 0xee, 0x91, 0xe5, 0x3f, 0x67, 0x39, 0x2f, 0x1b, 0x89, 0x9f, 0x04, 0x9d, 0x46, 0x3e, 0x70, 0x92, 0x9e, 0x8c, 0xf5
    };
    uint8_t s0s1s2[] = {
        0x03, 0xac, 0x44, 0x29, 0x53, 0x04, 0x05, 0x00, 0x01, 0x6e, 0x65, 0x69, 0x2d, 0x69, 0x2d, 0x69, 0x73, 0x6e, 0x69, 0x73, 0x6c, 0x65, 0x72, 0x69, 0x72, 0x76, 0x65, 0x72, 0x69, 0x77, 0x74, 0x2e, 0x6e, 0x72, 0x76, 0x72, 0x65, 0x72, 0x70, 0x72, 0x69, 0x69, 0x70, 0x72, 0x73, 0x6e, 0x65, 0x72, 0x72, 0x6e, 0x2d, 0x65, 0x74, 0x72, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x40, 0x69, 0x69, 0x76, 0x77, 0x2d, 0x73, 0x65, 0x72, 0x72, 0x76, 0x73, 0x72, 0x2e, 0x2d, 0x76, 0x65, 0x31, 0x65, 0x6d, 0x6d, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x72, 0x65, 0x2d, 0x74, 0x69, 0x31, 0x65, 0x2d, 0x6f, 0x77, 0x2e, 0x76, 0x77, 0x2d, 0x77, 0x72, 0x65, 0x65, 0x31, 0x74, 0x73, 0x70, 0x74, 0x6e, 0x72, 0x6e, 0x73, 0x6d, 0x2e, 0x69, 0x72, 0x2d, 0x65, 0x69, 0x77, 0x69, 0x76, 0x72, 0x77, 0x72, 0x32, 0x6e, 0x65, 0x6c, 0x2e, 0x2d, 0x6e, 0x69, 0x6d, 0x6c, 0x73, 0x65, 0x73, 0x70, 0x2d, 0x65, 0x72, 0x40, 0x72, 0x74, 0x6e, 0x6e, 0x6d, 0x6f, 0x70, 0x74, 0x73, 0x2d, 0x63, 0x69, 0x32, 0x31, 0x2d, 0x40, 0x69, 0x70, 0x2d, 0x2d, 0x72, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x69, 0x65, 0x6e, 0x32, 0x6f, 0x6c, 0x6e, 0x72, 0x73, 0x77, 0x65, 0x65, 0x72, 0x32, 0x6d, 0x65, 0x6c, 0x2d, 0x72, 0x6e, 0x65, 0x6d, 0x31, 0x65, 0x74, 0x2d, 0x6f, 0x72, 0x65, 0x63, 0x69, 0x40, 0x70, 0x2d, 0x65, 0x6d, 0x2d, 0x77, 0x63, 0x63, 0x74, 0x40, 0x36, 0x2d, 0x72, 0x65, 0x70, 0x2d, 0x6e, 0x69, 0x6d, 0x65, 0x74, 0x70, 0x76, 0x40, 0x76, 0x72, 0x72, 0x69, 0x77, 0x76, 0x69, 0x74, 0x74, 0x65, 0x31, 0x6d, 0x2e, 0x6f, 0x72, 0x73, 0x73, 0x6c, 0x40, 0x36, 0x72, 0x70, 0x72, 0x70, 0x72, 0x69, 0x32, 0x6c, 0x77, 0x70, 0x76, 0x65, 0x72, 0x76, 0x63, 0x65, 0x65, 0x77, 0x72, 0x6e, 0x2e, 0x76, 0x69, 0x69, 0x2e, 0x40, 0x72, 0x2e, 0x2e, 0x72, 0x73, 0x6e, 0x72, 0x72, 0x6e, 0x70, 0x40, 0x77, 0x65, 0x77, 0x65, 0x70, 0x63, 0x74, 0x2d, 0x70, 0x72, 0x2d, 0x74, 0x72, 0x31, 0x65, 0x6e, 0x2d, 0x76, 0x2d, 0x2d, 0x2d, 0x74, 0x76, 0x2d, 0x74, 0x65, 0x2e, 0x2d, 0x6c, 0x76, 0x2d, 0x6c, 0x70, 0x73, 0x6d, 0x65, 0x72, 0x31, 0x31, 0x36, 0x76, 0x73, 0x73, 0x6e, 0x2d, 0x6e, 0x73, 0x72, 0x2d, 0x6f, 0x6c, 0x65, 0x74, 0x77, 0x65, 0x69, 0x72, 0x69, 0x65, 0x6d, 0x76, 0x31, 0x65, 0x73, 0x72, 0x6c, 0x72, 0x77, 0x65, 0x76, 0x74, 0x72, 0x69, 0x72, 0x76, 0x32, 0x73, 0x6d, 0x72, 0x2d, 0x6d, 0x40, 0x69, 0x40, 0x69, 0x31, 0x69, 0x6f, 0x6e, 0x6d, 0x69, 0x73, 0x70, 0x72, 0x77, 0x6f, 0x6f, 0x65, 0x77, 0x76, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x32, 0x36, 0x6c, 0x74, 0x6e, 0x72, 0x74, 0x2d, 0x6e, 0x6c, 0x72, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x65, 0x2d, 0x6e, 0x70, 0x6e, 0x40, 0x65, 0x6e, 0x6e, 0x74, 0x65, 0x6e, 0x72, 0x6e, 0xfe, 0x5a, 0x38, 0x79, 0x81, 0xe8, 0x49, 0xee, 0x93, 0xbb, 0xa0, 0x59, 0x4a, 0xa0, 0xcc, 0x31, 0xbf, 0x0d, 0x86, 0xc0, 0x3f, 0xae, 0x2a, 0x16, 0xfa, 0xf0, 0x4e, 0x0f, 0xa3, 0x01, 0x06, 0xa0, 0x0e, 0xa5, 0x8c, 0xa4, 0xca, 0xd2, 0x01, 0xa5, 0x90, 0xbd, 0x55, 0xd1, 0x42, 0x2b, 0xd4, 0xb3, 0xbb, 0x06, 0xb1, 0x3a, 0x94, 0x41, 0x76, 0x1d, 0xa5, 0x23, 0x6e, 0x1e, 0x59, 0x73, 0x63, 0x34, 0x60, 0xd3, 0x48, 0xc0, 0x3b, 0xcf, 0xf1, 0xa8, 0x38, 0xd6, 0xf3, 0x5e, 0x6d, 0xcb, 0xea, 0xfc, 0x9c, 0x52, 0xae, 0x9a, 0x89, 0xdb, 0x24, 0x1b, 0x92, 0x4a, 0x85, 0x97, 0x3c, 0xd8, 0x4c, 0x31, 0xad, 0xfd, 0x00, 0xef, 0xc5, 0x17, 0xa5, 0x22, 0xc0, 0xf1, 0x94, 0x18, 0xec, 0xf6, 0x49, 0xe5, 0x05, 0x11, 0x12, 0x67, 0x6c, 0x71, 0xc0, 0x84, 0x6d, 0x50, 0xf8, 0x23, 0x01, 0x57, 0xc4, 0xfc, 0x73, 0x65, 0x69, 0x6e, 0x65, 0x72, 0x6d, 0x6f, 0x69, 0x2d, 0x65, 0x65, 0x69, 0x63, 0x63, 0x69, 0x2d, 0x72, 0x2d, 0x69, 0x2d, 0x2d, 0x77, 0x72, 0x76, 0x72, 0x72, 0x2d, 0x76, 0x70, 0x63, 0x69, 0x74, 0x73, 0x6d, 0x65, 0x6c, 0x2d, 0x73, 0x6c, 0x65, 0x6e, 0x73, 0x77, 0x69, 0x63, 0x69, 0x70, 0x31, 0x40, 0x72, 0x69, 0x2d, 0x2d, 0x2d, 0x72, 0x72, 0x6c, 0x72, 0x63, 0x72, 0x77, 0x6e, 0x6c, 0x2d, 0x72, 0x2e, 0x76, 0x72, 0x65, 0x6d, 0x76, 0x36, 0x6d, 0x72, 0x77, 0x72, 0x65, 0x65, 0x69, 0x72, 0x76, 0x6d, 0x76, 0x74, 0x76, 0x72, 0x65, 0x69, 0x72, 0x6e, 0x6d, 0x77, 0x6c, 0x40, 0x32, 0x70, 0x65, 0x65, 0x69, 0x72, 0x31, 0x2e, 0x70, 0x36, 0x31, 0x65, 0x70, 0x72, 0x72, 0x73, 0x72, 0x6e, 0x6e, 0x73, 0x32, 0x2d, 0x2d, 0x2d, 0x69, 0x65, 0x31, 0x74, 0x6e, 0x65, 0x74, 0x65, 0x76, 0x69, 0x6d, 0x6c, 0x6e, 0x70, 0x74, 0x73, 0x72, 0x6d, 0x72, 0x72, 0x69, 0x65, 0x74, 0x65, 0x65, 0x2d, 0x70, 0x74, 0x6e, 0x74, 0x65, 0x6f, 0x72, 0x69, 0x76, 0x40, 0x31, 0x69, 0x72, 0x6d, 0x6d, 0x77, 0x69, 0x72, 0x65, 0x6e, 0x40, 0x63, 0x40, 0x65, 0x65, 0x69, 0x2d, 0x72, 0x65, 0x40, 0x69, 0x32, 0x74, 0x73, 0x6e, 0x36, 0x2d, 0x70, 0x65, 0x6c, 0x70, 0x6e, 0x72, 0x69, 0x32, 0x65, 0x74, 0x76, 0x77, 0x73, 0x6f, 0x77, 0x65, 0x72, 0x2d, 0x6e, 0x73, 0x65, 0x65, 0x70, 0x65, 0x2d, 0x65, 0x73, 0x2d, 0x65, 0x2e, 0x73, 0x69, 0x67, 0x45, 0x8b, 0x6b, 0x3b, 0xc9, 0x5f, 0x09, 0x65, 0x65, 0x72, 0x6c, 0x73, 0x6d, 0x70, 0x70, 0x73, 0x63, 0x70, 0x40, 0x72, 0x76, 0x65, 0x6e, 0x6f, 0x6c, 0x69, 0x2e, 0x72, 0x73, 0x76, 0x69, 0x77, 0x72, 0x2d, 0x69, 0x6e, 0x69, 0x65, 0x77, 0x73, 0x69, 0x70, 0x77, 0x63, 0x65, 0x74, 0x72, 0x73, 0x31, 0x65, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x6d, 0x65, 0x36, 0x6e, 0x6e, 0x69, 0x6d, 0x6e, 0x70, 0x77, 0x72, 0x65, 0x31, 0x65, 0x6e, 0x6e, 0x65, 0x2d, 0x65, 0x65, 0x2e, 0x77, 0x6e, 0x6f, 0x2d, 0x76, 0x65, 0x72, 0x6c, 0x31, 0x76, 0x65, 0x72, 0x2d, 0x36, 0x6c, 0x70, 0x6f, 0x65, 0x72, 0x73, 0x63, 0x72, 0x77, 0x73, 0x72, 0x65, 0x65, 0x65, 0x6c, 0x76, 0x72, 0x65, 0x6e, 0x65, 0x2e, 0x6f, 0x2d, 0x72, 0x70, 0x65, 0x74, 0x72,
        0x77, 0x69, 0x69, 0x72, 0x65, 0x77, 0x6c, 0x72, 0x2d, 0x69, 0x72, 0x31, 0x6e, 0x65, 0x70, 0x72, 0x74, 0x76, 0x6c, 0x2e, 0x72, 0x65, 0x72, 0x6c, 0x73, 0x6c, 0x2e, 0x2e, 0x72, 0x2d, 0x6e, 0x63, 0x32, 0x2e, 0x65, 0x2d, 0x65, 0x69, 0x2d, 0x65, 0x70, 0x6e, 0x72, 0x72, 0x32, 0x2e, 0x73, 0x70, 0x77, 0x65, 0x73, 0x77, 0x73, 0x40, 0x40, 0x73, 0x63, 0x2e, 0x65, 0x76, 0x70, 0x65, 0x69, 0x65, 0x70, 0x73, 0x40, 0x65, 0x73, 0x2d, 0x2d, 0x2e, 0x2e, 0x73, 0x65, 0x6f, 0x65, 0x65, 0x6d, 0x76, 0x70, 0x6d, 0x69, 0x70, 0x70, 0x69, 0x2e, 0x76, 0x6e, 0x72, 0x72, 0x72, 0x6d, 0x73, 0x6f, 0x73, 0x72, 0x72, 0x72, 0x77, 0x70, 0x65, 0x69, 0x72, 0x73, 0x6e, 0x69, 0x65, 0x65, 0x74, 0x65, 0x69, 0x40, 0x63, 0x69, 0x70, 0x6c, 0x6e, 0x2d, 0x65, 0x69, 0x72, 0x63, 0x6c, 0x72, 0x2e, 0x36, 0x69, 0x72, 0x6c, 0x6c, 0x2d, 0x6f, 0x76, 0x69, 0x6f, 0x2d, 0x6d, 0x6c, 0x72, 0x72, 0x2e, 0x70, 0x73, 0x6d, 0x6f, 0x2e, 0x6e, 0x69, 0x65, 0x65, 0x2d, 0x6d, 0x76, 0x6e, 0x69, 0x73, 0x73, 0x73, 0x74, 0x63, 0x65, 0x76, 0x2e, 0x77, 0x2d, 0x36, 0x73, 0x69, 0x2d, 0x72, 0x72, 0x6c, 0x36, 0x74, 0x72, 0x6d, 0x65, 0x2d, 0x65, 0x2e, 0x6d, 0x31, 0x72, 0x6f, 0x74, 0x76, 0x31, 0x65, 0x6d, 0x69, 0x72, 0x69, 0x69, 0x2d, 0x72, 0x73, 0x72, 0x72, 0x76, 0x31, 0x6e, 0x2d, 0x69, 0x6e, 0x77, 0x70, 0x69, 0x72, 0x6e, 0x76, 0x74, 0x6f, 0x65, 0x63, 0x6f, 0x73, 0x65, 0x73, 0x72, 0x69, 0x69, 0x40, 0x6e, 0x65, 0x65, 0x65, 0x65, 0x77, 0x70, 0x70, 0x6e, 0x72, 0x6e, 0x65, 0x72, 0x32, 0x65, 0x2d, 0x77, 0x69, 0x6e, 0x70, 0x69, 0x6f, 0x76, 0x77, 0x72, 0x74, 0x77, 0x6e, 0x72, 0xfe, 0x98, 0xf3, 0xb4, 0xff, 0x3f, 0x2e, 0xdb, 0x59, 0xbd, 0x32, 0x02, 0x6a, 0x44, 0x03, 0x67, 0x9e, 0xe1, 0x98, 0x97, 0xed, 0x67, 0x6d, 0xb0, 0x8f, 0xa9, 0xb6, 0xf8, 0x4d, 0x92, 0x35, 0x19, 0x72, 0x72, 0x65, 0x74, 0x73, 0x6e, 0x65, 0x65, 0x69, 0x36, 0x72, 0x73, 0x2d, 0x70, 0x2d, 0x2d, 0x69, 0x6e, 0x72, 0x65, 0x32, 0x72, 0x77, 0x72, 0x73, 0x77, 0x73, 0x70, 0x2d, 0x2d, 0x69, 0x6c, 0x70, 0x74, 0x65, 0x69, 0x72, 0x74, 0x6e, 0x76, 0x65, 0x76, 0x76, 0x69, 0x69, 0x65, 0x70, 0x6e, 0x73, 0x6e, 0x36, 0x76, 0x70, 0x76, 0x6c, 0x6c, 0x70, 0x6e, 0x6e, 0x74, 0x2e, 0x6f, 0x32, 0x74, 0x76, 0x74, 0x40, 0x72, 0x6e, 0x72, 0x74, 0x74, 0x2d, 0x6f, 0x72, 0x73, 0x32, 0x72, 0x32, 0x72, 0x70, 0x65, 0x65, 0x6e, 0x72, 0x70, 0x73, 0x72, 0x69, 0x74, 0x74, 0x6e, 0x72, 0x6c, 0x31, 0x74, 0x77, 0x31, 0x63, 0x63, 0x74, 0x69, 0x72, 0x69, 0x72, 0x70, 0x31, 0x74, 0x72, 0x76, 0x65, 0x72, 0x65, 0x6c, 0x76, 0x6d, 0x72, 0x6c, 0x69, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x69, 0x6c, 0x74, 0x6e, 0x65, 0x69, 0x77, 0x73, 0x70, 0x69, 0x72, 0x2d, 0x65, 0x74, 0x2e, 0x65, 0x65, 0x6d, 0x72, 0x31, 0x2d, 0x72, 0x36, 0x65, 0x2d, 0x69, 0x6d, 0x36, 0x6e, 0x72, 0x6d, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x6e, 0x31, 0x6e, 0x40, 0x72, 0x40, 0x6f, 0x73, 0x6d, 0x36, 0x2e, 0x72, 0x65, 0x36, 0x74, 0x77, 0x65, 0x65, 0x73, 0x36, 0x76, 0x6c, 0x6f, 0x2d, 0x36, 0x6d, 0x36, 0x70, 0x32, 0x74, 0x6d, 0x65, 0x6d, 0x69, 0x65, 0x65, 0x69, 0x76, 0x69, 0x74, 0x2d, 0x63, 0x2d, 0x6e, 0x32, 0x72, 0x63, 0x2d, 0x77, 0x72, 0x74, 0x72, 0x70, 0x6e, 0x76, 0x6f, 0x72, 0x40, 0x65, 0x65, 0x6d, 0x77, 0x2d, 0x2d, 0x74, 0x6e, 0x73, 0x76, 0x65, 0x69, 0x69, 0x72, 0x6f, 0x65, 0x70, 0x69, 0x6d, 0x76, 0x69, 0x65, 0x72, 0x2d, 0x74, 0x2d, 0x69, 0x65, 0x72, 0x69, 0x6f, 0x72, 0x72, 0x69, 0x76, 0x72, 0x77, 0x69, 0x2e, 0x77, 0x69, 0x70, 0x69, 0x6d, 0x36, 0x72, 0x76, 0x65, 0x76, 0x73, 0x6e, 0x72, 0x65, 0x2e, 0x76, 0x2d, 0x76, 0x6f, 0x2d, 0x65, 0x73, 0x72, 0x74, 0x36, 0x2d, 0x6f, 0x70, 0x73, 0x74, 0x74, 0x77, 0x6c, 0x2d, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x72, 0x32, 0x2d, 0x72, 0x69, 0x6d, 0x6e, 0x72, 0x6c, 0x6f, 0x65, 0x36, 0x31, 0x65, 0x65, 0x69, 0x73, 0x31, 0x74, 0x69, 0x69, 0x65, 0x40, 0x69, 0x6e, 0x2d, 0x63, 0x40, 0x31, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x72, 0x65, 0x65, 0x76, 0x65, 0x70, 0x72, 0x6c, 0x2d, 0x6e, 0x73, 0x69, 0x65, 0x65, 0x6e, 0x2e, 0x63, 0x6c, 0x72, 0x65, 0x2d, 0x2e, 0x6d, 0x72, 0x76, 0x70, 0x69, 0x6d, 0x40, 0x32, 0x77, 0x72, 0x6e, 0x72, 0x6c, 0x36, 0x72, 0x31, 0x2d, 0x73, 0x74, 0x2d, 0x69, 0x63, 0x40, 0x70, 0x32, 0x65, 0x31, 0x69, 0x69, 0x65, 0x72, 0x63, 0x74, 0x72, 0x74, 0x77, 0x6e, 0x69, 0x72, 0x65, 0x76, 0x65, 0x77, 0x69, 0x69, 0x73, 0x6e, 0x77, 0x77, 0x73, 0x6f, 0x69, 0x70, 0x73, 0x2d, 0x65, 0x65, 0x73, 0x65, 0x77, 0x2d, 0x73, 0x72, 0x6d, 0x65, 0x32, 0x6e, 0x73, 0x36, 0x65, 0x72, 0x77, 0x70, 0x65, 0x69, 0x2d, 0x2d, 0x74, 0x6f, 0x6f, 0x36, 0x63, 0x74, 0x72, 0x63, 0x77, 0x69, 0x2e, 0x31, 0x6c, 0x65, 0x77, 0x72, 0x65, 0x76, 0x74, 0x2d, 0x77, 0x2e, 0x76, 0x72, 0x6e, 0x36, 0x70, 0x69, 0x2e, 0x6e, 0x72, 0x77, 0x69, 0x65, 0x74, 0x2d, 0x6e, 0x63, 0x6e, 0x70, 0x2d, 0x6e, 0x2e, 0x6d, 0x69, 0x65, 0x63, 0x65, 0x2d, 0x76, 0x70, 0x73, 0x31, 0x73, 0x2d, 0x76, 0x6e, 0x6e, 0x6c, 0x2d, 0x6c, 0x2d, 0x65, 0x6e, 0x73, 0x6c, 0x65, 0x74, 0x70, 0x65, 0x2d, 0x6e, 0x77, 0x76, 0x40, 0x69, 0x40, 0x63, 0x6f, 0x72, 0x32, 0x6d, 0x31, 0x72, 0x36, 0x69, 0x73, 0x72, 0x70, 0x65, 0x2d, 0x6c, 0x2e, 0x72, 0x74, 0x74, 0x65, 0x69, 0x6f, 0x69, 0x2d, 0x2d, 0x65, 0x6f, 0x65, 0x74, 0x72, 0x69, 0x76, 0x72, 0x72, 0x65, 0x69, 0x76, 0x69, 0x69, 0x6e, 0x31, 0x65, 0x76, 0x72, 0x73, 0x77, 0x72, 0x2d, 0x69, 0x65, 0x69, 0x70, 0x65, 0x6e, 0x6e, 0x65, 0x65, 0x6e, 0x2d, 0x72, 0x76, 0x72, 0x6c, 0x2e, 0x70, 0x76, 0x6e, 0x69, 0x72, 0x70, 0x73, 0x2d, 0x69, 0x74, 0x76, 0x72, 0x70, 0x65, 0x63, 0x72, 0x70, 0x6e, 0x36, 0x6c, 0x74, 0x72, 0x72, 0x72, 0x73, 0x65, 0x40, 0x63, 0x6d, 0x63, 0x32, 0x65, 0x32, 0x69, 0x6e,
        0x77, 0x65, 0x74, 0x72, 0x77, 0x40, 0x69, 0x65, 0x70, 0x31, 0x36, 0x72, 0x73, 0x2d, 0x72, 0x72, 0x32, 0x72, 0x6c, 0x77, 0x6e, 0x6f, 0x77, 0x6c, 0x74, 0x72, 0x2d, 0x6e, 0x65, 0x70, 0x6c, 0x72, 0x6f, 0x69, 0x2d, 0x2d, 0x69, 0x36, 0x69, 0x69, 0x76, 0x69, 0x69, 0x6d, 0x72, 0x73, 0x6f, 0x6d, 0x74, 0x70, 0x76, 0x6d, 0x6d, 0x69, 0x72, 0x70, 0x70, 0x2d, 0x31, 0x63, 0x6c, 0x65, 0x65, 0x6e, 0x2d, 0x77, 0x74, 0x73, 0x6c, 0x72, 0x6e, 0x65, 0x65, 0x2d, 0x6c, 0x69, 0x2d, 0x6e, 0x74, 0x70, 0x72, 0x77, 0x77, 0x65, 0x65, 0x65, 0x2d, 0x76, 0x6e, 0x72, 0x69, 0x69, 0x73, 0x65, 0x74, 0x73, 0x76, 0x72, 0x72, 0x72, 0x69, 0x72, 0x73, 0x72, 0x6f, 0x2e, 0x77, 0x2d, 0x2d, 0x6c, 0x6e, 0x65, 0x65, 0x6d, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x40, 0x69, 0x32, 0x69, 0x32, 0x6e, 0x65, 0x32, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x36, 0x6e, 0x72, 0x32, 0x6e, 0x65, 0x69, 0x32, 0x6f, 0x70, 0x72, 0x72, 0x65, 0x72, 0x72, 0x69, 0x6e, 0x6d, 0x69, 0x70, 0x6c, 0x6c, 0x65, 0x31, 0x72, 0x72, 0x73, 0x72, 0x70, 0x73, 0x72, 0x65, 0x65, 0x6e, 0x76, 0x69, 0x6d, 0x65, 0x6c, 0x65, 0x31, 0x74, 0x74, 0x72, 0x63, 0x2e, 0x69, 0x65, 0x2d, 0x6d, 0x72, 0x70, 0x6e, 0x6c, 0x65, 0x31, 0x73, 0x73, 0x40, 0x74, 0x72, 0x73, 0x2e, 0x74, 0x6e, 0x77, 0x6c, 0x6f, 0x70, 0x77, 0x76, 0x73, 0x72, 0x69, 0x77, 0x69, 0x6e, 0x69, 0x2d, 0x72, 0x70, 0x70, 0x73, 0x2e, 0x76, 0x73, 0x65, 0x72, 0x72, 0x74, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x77, 0x72, 0x65, 0x2d, 0x72, 0x69, 0x36, 0x77, 0x77, 0x77, 0x40, 0x2d, 0x6d, 0x69, 0x74, 0x72, 0x2d, 0x32, 0x6f, 0x76, 0x72, 0x2d, 0x2d, 0x65, 0x2e, 0x2e, 0x72, 0x6e, 0x32, 0x74, 0x6c, 0x6e, 0x6c, 0x2e, 0x6d, 0x2d, 0x6f, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x65, 0x69, 0x40, 0x69, 0x77, 0x65, 0x6c, 0x2d, 0x69, 0x69, 0x65, 0x72, 0x72, 0x32, 0x40, 0x73, 0x65, 0x36, 0x76, 0x73, 0x72, 0x69, 0x63, 0x77, 0x72, 0x6c, 0x72, 0x6e, 0x74, 0x2d, 0x65, 0x69, 0x72, 0x70, 0x6d, 0x65, 0x6c, 0x73, 0x65, 0x6c, 0x32, 0x2d, 0x73, 0x70, 0x2d, 0x31, 0x72, 0x74, 0x2e, 0x65, 0x74, 0x72, 0x74, 0x72, 0x70, 0x69, 0x40, 0x36, 0x2d, 0x74, 0x72, 0x6c, 0x2d, 0x6e, 0x72, 0x6e, 0x6d, 0x63, 0x76, 0x74, 0x6d, 0x70, 0x32, 0x70, 0x69, 0x69, 0x2d, 0x73, 0x72, 0x74, 0x65, 0x74, 0x74, 0x70, 0x2d, 0x31, 0x6c, 0x77, 0x65, 0x72, 0x70, 0x73, 0x36, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x76, 0x69, 0x2e, 0x6e, 0x72, 0x72, 0x36, 0x65, 0x69, 0x72, 0x69, 0x40, 0x6c, 0x74, 0x6c, 0x72, 0x2d, 0x70, 0x74, 0x76, 0x74, 0x6f, 0x72, 0x31, 0x73, 0x70, 0x65, 0x74, 0x69, 0x6e, 0x69, 0x6c, 0x70, 0x72, 0x65, 0x70, 0x72, 0x73, 0x69, 0x2d, 0x6d, 0x63, 0x2d, 0x72, 0x2d, 0x36, 0x73, 0x6e, 0x2d, 0x6d, 0x69, 0x76, 0x76, 0x6d, 0x74, 0x72, 0x77, 0x74, 0x2e, 0x6d, 0x65, 0x2d, 0x65, 0x6d, 0x2e, 0x6c, 0x73, 0x6e, 0x6f, 0x76, 0x31, 0x74, 0x65, 0x65, 0x31, 0x69, 0x65, 0x32, 0x2d, 0x74, 0x2d, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x32, 0x72, 0x73, 0x74, 0x65, 0x65, 0x69, 0x73, 0x77, 0x77, 0x2e, 0x6e, 0x72, 0x65, 0x70, 0x76, 0x40, 0x77, 0x65, 0x2d, 0x70, 0x36, 0x2d, 0x74, 0x65, 0x2d, 0x69, 0x74, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x73, 0x31, 0x36, 0x69, 0x31, 0x74, 0x76, 0x65, 0x77, 0x6c, 0x6e, 0x6c, 0x32, 0x6e, 0x70, 0x73, 0x69, 0x69, 0x65, 0x72, 0x2d, 0x6e, 0x2d, 0x65, 0x65, 0x6c, 0x32, 0x77, 0x72, 0x69, 0x70, 0x76, 0x32, 0x65, 0x6c, 0x36, 0x65, 0x69, 0x31, 0x6e, 0x72, 0x6c, 0x6d, 0x65, 0x65, 0x77, 0x6e, 0x2d, 0x32, 0x77, 0x69, 0x65, 0x6d, 0x74, 0x77, 0x40, 0x65, 0x6e, 0x77, 0x73, 0x65, 0x72, 0x6c, 0x40, 0x65, 0x65, 0x72, 0x72, 0x74, 0x6e, 0x6c, 0x6d, 0x73, 0x69, 0x76, 0x72, 0x31, 0x2d, 0x65, 0x36, 0x72, 0x2d, 0x70, 0x69, 0x6e, 0x63, 0x31, 0x2d, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x2e, 0x77, 0x2d, 0x72, 0x76, 0x63, 0x69, 0x2d, 0x6d, 0x70, 0x2d, 0x6c, 0x69, 0x63, 0x69, 0x77, 0x6e, 0x69, 0x77, 0x36, 0x72, 0x69, 0x72, 0x2e, 0x74, 0x72, 0x6e, 0x65, 0x6f, 0x73, 0x2d, 0x2e, 0x72, 0x63, 0x76, 0x74, 0x36, 0x65, 0x72, 0x65, 0x6d, 0x32, 0x72, 0x70, 0x40, 0x65, 0x74, 0x6e, 0x32, 0x70, 0x2d, 0x31, 0x40, 0x6c, 0x65, 0x6c, 0x76, 0x69, 0x69, 0x76, 0x76, 0x73, 0x31, 0x6e, 0x65, 0x74, 0x65, 0x6d, 0x69, 0x2d, 0x72, 0x74, 0x74, 0x6c, 0x31, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x36, 0x69, 0x69, 0x72, 0x6e, 0x2d, 0x2d, 0x2d, 0x72, 0x73, 0x76, 0x72, 0x72, 0x65, 0x72, 0x65, 0x72, 0x2d, 0x6c, 0x76, 0x77, 0x63, 0x77, 0x72, 0x6d, 0x72, 0x2e, 0x65, 0x73, 0x32, 0x72, 0x36, 0x77, 0x72, 0x72, 0x6d, 0x74, 0x2d, 0x72, 0x2e, 0x73, 0x73, 0x65, 0x77, 0x6e, 0x65, 0x69, 0x65, 0x2d, 0x65, 0x77, 0x6f, 0x74, 0x72, 0x32, 0x40, 0x6e, 0x72, 0x69, 0x6e, 0x32, 0x70, 0x73, 0x72, 0x40, 0x2d, 0x65, 0x69, 0x65, 0x77, 0x65, 0x70, 0x40, 0x36, 0x72, 0x6c, 0x6d, 0x73, 0x69, 0x72, 0x72, 0x74, 0x36, 0x6c, 0x76, 0x65, 0x76, 0x2d, 0x74, 0x6c, 0x72, 0x72, 0x74, 0x6e, 0x73, 0x74, 0x69, 0x72, 0x6d, 0x40, 0x2d, 0x6e, 0x70, 0x73, 0x2d, 0x6d, 0x72, 0x72, 0x70, 0x65, 0x65, 0x36, 0x6e, 0x77, 0x2d, 0x69, 0x2d, 0x32, 0x72, 0x6d, 0x72, 0x6c, 0x32, 0x6c, 0x73, 0x6d, 0x65, 0x36, 0x69, 0x69, 0x72, 0x77, 0x74, 0x6f, 0x72, 0x6d, 0x6d, 0x69, 0x65, 0x73, 0x63, 0x65, 0x74, 0x74, 0x72, 0x65, 0x72, 0x2e, 0x6e, 0x73, 0x65, 0x76, 0x6c, 0x76, 0x77, 0x72, 0x6e, 0x6c, 0x32, 0x2d, 0x73, 0x65, 0x73, 0x2e, 0x76, 0x72, 0x65, 0x2d, 0x72, 0x77, 0x2d, 0x77, 0x70, 0x65, 0x6c, 0x72, 0x6e, 0x2e, 0x31, 0x73, 0x2e, 0x72, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x6d, 0x32, 0x70, 0x76, 0x76, 0x31, 0x76, 0x77, 0x65, 0x6e, 0x73, 0x63, 0x2e, 0x2d, 0x69, 0x6e, 0x69, 0x77, 0x6e, 0x65, 0x6d, 0x2d, 0x72, 0x6e, 0x74, 0x6e, 0x40, 0x73, 0x2d, 0x74, 0x74, 0x65, 0x72, 0x2d, 0x2d, 0x69, 0x73, 0x70,
        0x69, 0x6c, 0x72, 0x76, 0x6d, 0x74, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x63, 0x69, 0x65, 0x65, 0x72, 0x6f, 0x6e, 0x72, 0x72, 0x6c, 0x6e, 0x6e, 0x65, 0x6d, 0x74, 0x6c, 0x74, 0x65, 0x69, 0x2d, 0x6f, 0x69, 0x2e, 0x6e, 0x63, 0x65, 0x6c, 0x40, 0x70, 0x2d, 0x2d, 0x74, 0x73, 0x74, 0x40, 0x72, 0x74, 0x6c, 0x72, 0x6e, 0x6f, 0x73, 0x65, 0x74, 0x6d, 0x69, 0x32, 0x72, 0x65, 0x77, 0x6e, 0x76, 0x74, 0x73, 0x2d, 0x72, 0x6e, 0x69, 0x73, 0x40, 0x36, 0x2d, 0x6d, 0x2e, 0x65, 0x6d, 0x40, 0x69, 0x72, 0x72, 0x70, 0x65, 0x72, 0x76, 0x6c, 0x65, 0x76, 0x72, 0x65, 0x69, 0x65, 0x69, 0x6e, 0x72, 0x2d, 0x63, 0x72, 0x69, 0x6e, 0x72, 0x69, 0x6e, 0x69, 0x70, 0x6e, 0x2d, 0x69, 0x6c, 0x72, 0x2d, 0x65, 0x2d, 0x72, 0x6f, 0x65, 0x6e, 0x76, 0x6e, 0x40, 0x2d, 0x65, 0x72, 0x72, 0x6f, 0x6f, 0x72, 0x6c, 0x65, 0x74, 0x73, 0x72, 0x70, 0x77, 0x69, 0x69, 0x6d, 0x6c, 0x6d, 0x6e, 0x2d, 0x65, 0x65, 0x65, 0x74, 0x6c, 0x2d, 0x74, 0x6f, 0x2d, 0x74, 0x70, 0x72, 0x6e, 0x73, 0x72, 0x69, 0x72, 0x2e, 0x6d, 0x69, 0x65, 0x65, 0x32, 0x70, 0x6c, 0x6c, 0x65, 0x77, 0x2d, 0x72, 0x6f, 0x70, 0x76, 0x65, 0x2d, 0x72, 0x69, 0x6d, 0x72, 0x36, 0x40, 0x6d, 0x72, 0x6c, 0x6d, 0x77, 0x6c, 0x6e, 0x69, 0x72, 0x6d, 0x76, 0x73, 0x2e, 0x73, 0x72, 0x77, 0x73, 0x76, 0x2d, 0x73, 0x76, 0x6d, 0x76, 0x65, 0x69, 0x76, 0x63, 0x65, 0x72, 0x31, 0x72, 0x69, 0x76, 0x72, 0x65, 0x65, 0x2d, 0x73, 0x6d, 0x31, 0x72, 0x6e, 0x72, 0x2d, 0x2d, 0x36, 0x72, 0x73, 0x77, 0x2d, 0x77, 0x36, 0x76, 0x72, 0x6d, 0x65, 0x2d, 0x72, 0x70, 0x2d, 0x74, 0x32, 0x6c, 0x63, 0x6d, 0x6f, 0x6e, 0x2e, 0x2d, 0x69, 0x65, 0x73, 0x6d, 0x65, 0x73, 0x6e, 0x6d, 0x6c, 0x65, 0x6e, 0x72, 0x72, 0x72, 0x32, 0x70, 0x65, 0x73, 0x6c, 0x6d, 0x70, 0x6d, 0x72, 0x6f, 0x65, 0x6c, 0x76, 0x73, 0x63, 0x73, 0x65, 0x6c, 0x2d, 0x6e, 0x72, 0x65, 0x65, 0x72, 0x2d, 0x70, 0x6d, 0x69, 0x69, 0x65, 0x2d, 0x6c, 0x72, 0x69, 0x6c, 0x2d, 0x74, 0x65, 0x65, 0x69, 0x31, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x74, 0x73, 0x65, 0x32, 0x2d, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x65, 0x69, 0x73, 0x32, 0x6d, 0x65, 0x2d, 0x65, 0x74, 0x6d, 0x6d, 0x73, 0x76, 0x6c, 0x69, 0x65, 0x2d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x72, 0x74, 0x31, 0x2d, 0x76, 0x73, 0x2e, 0x2d, 0x2d, 0x72, 0x76, 0x77, 0x65, 0x72, 0x72, 0x40, 0x6e, 0x6c, 0x6d, 0x72, 0x74, 0x73, 0x72, 0x72, 0x65, 0x65, 0x2d, 0x6f, 0x74, 0x70, 0x63, 0xb8, 0xa1, 0x11, 0x6e, 0xd7, 0x74, 0x16, 0x7f, 0xb4, 0xba, 0x40, 0x93, 0x98, 0x00, 0x71, 0xcc, 0x42, 0xa7, 0x2f, 0x28, 0x69, 0xe7, 0x31, 0x48, 0x22, 0xa0, 0xe1, 0x45, 0xe3, 0xf7, 0x7f, 0x3a
    };
    uint8_t c2[] = {
        0x5b, 0x52, 0xf1, 0x2d, 0x94, 0xcb, 0xb0, 0x86, 0xd8, 0xd3, 0xe3, 0x20, 0x88, 0x47, 0xcf, 0x5a, 0x49, 0xd2, 0x11, 0x30, 0x92, 0x17, 0x8d, 0xf4, 0x99, 0xf7, 0x6c, 0x8a, 0xbc, 0xe7, 0x5c, 0x58, 0x6a, 0x65, 0xed, 0x81, 0xdc, 0xdd, 0xcf, 0x83, 0xcd, 0xa4, 0xed, 0xa2, 0x5e, 0x63, 0xd9, 0x98, 0xf6, 0x2e, 0x15, 0x76, 0x9a, 0xc8, 0x8c, 0x42, 0x54, 0x44, 0xf4, 0x47, 0xf5, 0x96, 0xc9, 0x6e, 0x23, 0x09, 0x1a, 0x0d, 0xe3, 0x04, 0xe6, 0xed, 0x48, 0x49, 0x62, 0x31, 0xe8, 0x36, 0x04, 0xed, 0xb9, 0xe7, 0xa6, 0x35, 0x4d, 0xcd, 0xe3, 0xfa, 0xa0, 0xc8, 0x34, 0xbd, 0x62, 0x7b, 0xbc, 0xbe, 0x1c, 0x5b, 0x69, 0x1f, 0x9c, 0x30, 0x20, 0x48, 0x52, 0xd1, 0xb6, 0x5e, 0xa2, 0x6e, 0x06, 0x94, 0x72, 0x10, 0x56, 0x7c, 0x94, 0xa5, 0xc0, 0xaa, 0xea, 0x48, 0x61, 0x03, 0x14, 0x94, 0x09, 0x77, 0xd9, 0xa7, 0xfe, 0x78, 0x17, 0x95, 0x4f, 0x7e, 0xb0, 0x32, 0x63, 0x02, 0x17, 0x47, 0x1e, 0x7d, 0xb2, 0x7d, 0xb5, 0xcb, 0x9f, 0x61, 0x65, 0xed, 0x03, 0xd2, 0xdb, 0xd1, 0xb3, 0xd6, 0x1a, 0xf5, 0x67, 0x0b, 0x8b, 0x6b, 0x44, 0xf2, 0x62, 0x42, 0xc2, 0x4d, 0xe1, 0x5c, 0xfe, 0xc6, 0x19, 0x2b, 0xfb, 0x03, 0x0f, 0x1b, 0x89, 0x08, 0x86, 0x40, 0xca, 0x45, 0x15, 0xda, 0x65, 0xcc, 0x73, 0x00, 0x49, 0x4e, 0x48, 0x21, 0x25, 0xc6, 0xde, 0x26, 0x21, 0x1d, 0xea, 0x3c, 0x11, 0xac, 0xef, 0x34, 0x4c, 0x96, 0xcc, 0x5e, 0x26, 0xf3, 0xcd, 0x70, 0x0d, 0x62, 0xea, 0x09, 0x35, 0x2b, 0x1e, 0x60, 0xe4, 0x76, 0xd3, 0x65, 0x01, 0x8c, 0xab, 0xd4, 0x89, 0xad, 0x81, 0x9d, 0x04, 0x01, 0xd5, 0x55, 0x3c, 0xcb, 0x32, 0xe1, 0xb5, 0xd4, 0xda, 0xb4, 0xa9, 0x01, 0xb2, 0x10, 0xc7, 0xb1, 0xa9, 0x54, 0x66, 0x1d, 0xcc, 0xff, 0x54, 0x0b, 0x84, 0x37, 0xe0, 0x3a, 0xa5, 0x68, 0x80, 0x87, 0xbc, 0x3c, 0x0f, 0xda, 0x7e, 0x3c, 0x23, 0xfc, 0xd8, 0xc5, 0x52, 0xf7, 0x22, 0x12, 0x05, 0x9c, 0x68, 0x39, 0xb1, 0xed, 0x26, 0x24, 0x2b, 0x7e, 0x0b, 0xaf, 0x9e, 0x97, 0x45, 0x7b, 0xa9, 0xbc, 0x48, 0x0e, 0x66, 0x93, 0x32, 0x0d, 0x6b, 0xd6, 0xf0, 0x4f, 0x54, 0x18, 0xcd, 0xc9, 0x8c, 0xce, 0xc4, 0xa2, 0xff, 0x1e, 0x69, 0x17, 0x7e, 0xf4, 0x99, 0x09, 0x68, 0xa1, 0x9e, 0x1f, 0xbf, 0x90, 0xdc, 0x77, 0x5d, 0x50, 0x2b, 0x0e, 0xff, 0x96, 0xdc, 0x21, 0x2e, 0x74, 0x22, 0x28, 0x88, 0xa0, 0x00, 0x32, 0x15, 0xb0, 0xfd, 0xb1, 0xc9, 0x75, 0xb3, 0x3c, 0xbd, 0x89, 0xc5, 0xa4, 0x48, 0x17, 0xa9, 0xc9, 0x50, 0x61, 0x0c, 0x35, 0x31, 0x55, 0x11, 0xe3, 0x23, 0xe9, 0x3e, 0x78, 0x25, 0xdc, 0x50, 0xe8, 0x23, 0x5f, 0xb7, 0x3f, 0xc7, 0xae, 0xf0, 0x82, 0x35, 0x46, 0x34, 0x63, 0xcc, 0x5d, 0x96, 0xb8, 0x6a, 0x7a, 0x7f, 0x54, 0x27, 0x1a, 0xa4, 0x63, 0xdd, 0xb0, 0xb6, 0x17, 0x08, 0xa1, 0x2e, 0x95, 0x9e, 0xd4, 0x9b, 0x71, 0x83, 0x81, 0x6c, 0xea, 0xab, 0x00, 0x2e, 0xca, 0x60, 0xc1, 0x4b, 0x83, 0xa7, 0xab, 0x47, 0xe8, 0x1b, 0x5a, 0x78, 0x4f, 0xec, 0xbd, 0x62, 0x94, 0x25, 0x75, 0x2e, 0x64, 0xe7, 0x70, 0x13, 0xac, 0xe9, 0x89, 0x4f, 0x1e, 0x79, 0xbc, 0x15, 0x0c, 0x8d, 0x40, 0xe8, 0x16, 0x31, 0x7c, 0xb8, 0xa5, 0xd7, 0x21, 0x39, 0x93, 0x9b, 0xe6, 0x05, 0x81, 0xb6, 0x20, 0xa8, 0x5d, 0x73, 0x58, 0x8b, 0x66, 0x92, 0xac, 0x23, 0xa0, 0xf4, 0x8c, 0xab, 0x58, 0xae, 0xb6, 0x9c, 0x3c, 0x4d, 0x77, 0x5f, 0xae, 0xe2, 0x57, 0x89, 0x8f, 0xe4, 0x68, 0x81, 0x24, 0x7d, 0x3b, 0x99, 0x46, 0x9f, 0x7b, 0x9d, 0xa6, 0xdd, 0x99, 0xcf, 0xc1, 0x79, 0x04, 0x95, 0xce, 0x96, 0x7a, 0xd9, 0xb5, 0x6e, 0xcf, 0xd1, 0x72, 0x18, 0x97, 0x76, 0xe2, 0xb7, 0x38, 0x1e, 0x24, 0x0b, 0x09, 0x00, 0x8b, 0x28, 0x5d, 0xf8, 0xd0, 0x50, 0x7f, 0xeb, 0x3b, 0x37, 0x61, 0x0b, 0xd3, 0xff, 0x65, 0x7d, 0x88, 0x1e, 0x1d, 0xbb, 0x6c, 0xf5, 0xf8, 0xf3, 0x2b, 0x51, 0xd9, 0x6d, 0xc9, 0xbe, 0xbe, 0xd1, 0x94, 0x0e, 0x58, 0x2a, 0x0a, 0xe4, 0xf8, 0x28, 0x26, 0xc3, 0x74, 0x87, 0xd3, 0x81, 0x48, 0x6e, 0x9b, 0xd5, 0xa1, 0x60, 0x87, 0xfc, 0x1b, 0x06, 0x33, 0x0d, 0x87, 0xfa, 0x9b, 0xf9, 0x73, 0x6b, 0x0c, 0xdf, 0xea, 0xee, 0x32, 0x78, 0xe0, 0xf8, 0x18, 0x3f, 0xc3, 0x3b, 0x12, 0x88, 0x0b, 0xb2, 0x4a, 0x52, 0x64, 0x4e, 0x58, 0x54, 0x82, 0x52, 0x61, 0x54, 0x28, 0x1b, 0xf7, 0x99, 0x06, 0xa2, 0xad, 0x04, 0x19, 0x9f, 0x2e, 0x34, 0xe6, 0xf0, 0xee, 0xeb, 0x93, 0x9a, 0x9c, 0x73, 0x86, 0x23, 0x6d, 0x5d, 0xae, 0x64, 0xec, 0x6f, 0xf9, 0x7c, 0xc7, 0x46, 0x96, 0xdb, 0x44, 0xf4, 0xab, 0xc9, 0x67, 0x61, 0xb8, 0xec, 0xf0, 0x99, 0xe0, 0x4d, 0x45, 0xed, 0xa3, 0x1c, 0xe9, 0x68, 0x31, 0x85, 0xa5, 0xa1, 0xba, 0x08, 0xdb, 0x3f, 0x84, 0x75, 0x70, 0x24, 0xcd, 0x49, 0xd4, 0x07, 0xa8, 0xaa, 0x52, 0xd9, 0x55, 0x68, 0x8f, 0x78, 0xd2, 0x5d, 0x46, 0x23, 0x60, 0x76, 0xe1, 0x22, 0xdc, 0x2a, 0xeb, 0xac, 0xbc, 0xeb, 0xd6, 0x4c, 0x0f, 0xb5, 0xcb, 0x47, 0xce, 0x43, 0x59, 0x1d, 0x3e, 0xfc, 0x7f, 0x7c, 0x93, 0x9e, 0xef, 0xcd, 0x79, 0x5c, 0x08, 0x8e, 0xeb, 0xa8, 0x98, 0x3e, 0x95, 0xd1, 0x36, 0x42, 0x57, 0xfd, 0x6d, 0xdc, 0xe0, 0xa3, 0x3f, 0x46, 0x32, 0xb7, 0xff, 0x00, 0x4f, 0x7b, 0x23, 0x4d, 0xd0, 0xe5, 0xdd, 0x40, 0xab, 0xb2, 0xcb, 0x45, 0x92, 0x76, 0x7c, 0x5b, 0x98, 0xc7, 0xc0, 0x54, 0x34, 0x94, 0x8e, 0xbb, 0x28, 0xcf, 0xba, 0xd9, 0xa0, 0xe6, 0xf3, 0x65, 0x61, 0xd7, 0x10, 0xd3, 0xeb, 0xce, 0x21, 0x6a, 0xca, 0x61, 0xe7, 0x81, 0x15, 0x18, 0x4e, 0x71, 0xb0, 0x99, 0x62, 0xd9, 0xeb, 0xd0, 0x8b, 0xe9, 0xdf, 0x6a, 0x6d, 0x59, 0x0b, 0x45, 0x93, 0x38, 0xfe, 0xe6, 0x6a, 0xd1, 0x5f, 0xb6, 0xe9, 0x86, 0x01, 0x38, 0xab, 0x59, 0x5c, 0xd7, 0xb7, 0xfa, 0x81, 0x8a, 0xbe, 0xdc, 0xeb, 0x50, 0x7d, 0x81, 0xfa, 0x1b, 0x8f, 0xce, 0x53, 0x38, 0xe4, 0x8a, 0x82, 0xbe, 0x7d, 0xdc, 0xd8, 0x57, 0x5a, 0x48, 0xa3, 0x38, 0x74, 0x8a, 0xac, 0xf2, 0xfd, 0xbf, 0xcc, 0xd8, 0x08, 0x4d, 0x3e, 0xae, 0xa9, 0x00, 0x66, 0x06, 0xcb, 0xf3,
        0x50, 0xcc, 0x52, 0xc7, 0x4b, 0x16, 0x33, 0xa5, 0xde, 0x20, 0xed, 0x6a, 0xa7, 0x58, 0x5e, 0x4e, 0x7e, 0x29, 0xab, 0xb9, 0x65, 0x9d, 0x17, 0xe0, 0x1e, 0x79, 0x77, 0xf6, 0x1e, 0xa0, 0xcb, 0x0c, 0xf7, 0xc0, 0xe4, 0xf6, 0x3b, 0x60, 0x81, 0xfe, 0xed, 0xd9, 0x42, 0xa9, 0x61, 0x9d, 0xa8, 0xd7, 0xe8, 0xaa, 0x97, 0xad, 0xbb, 0xba, 0x13, 0x6e, 0x05, 0xa5, 0xce, 0x7a, 0x65, 0x6f, 0x55, 0xe3, 0xcf, 0xbc, 0x67, 0x14, 0x64, 0x57, 0x9c, 0x46, 0x14, 0xd6, 0x1d, 0x39, 0x1c, 0xd3, 0xe8, 0x98, 0x20, 0x5a, 0x1a, 0x05, 0x3b, 0x27, 0xd5, 0x84, 0xca, 0xd4, 0x0b, 0xc4, 0x1e, 0xd8, 0x46, 0x29, 0x48, 0x95, 0xdb, 0xe5, 0x58, 0x8a, 0x51, 0xc7, 0x74, 0x7f, 0x53, 0xa8, 0xbb, 0x58, 0xc0, 0x5b, 0xe1, 0xa7, 0x27, 0x36, 0x6c, 0xa6, 0x70, 0xec, 0x88, 0xcd, 0x9a, 0x70, 0xe1, 0xa0, 0xc7, 0xdd, 0x60, 0x71, 0xf4, 0x2a, 0x51, 0x98, 0x8e, 0xab, 0xb8, 0x13, 0x03, 0x48, 0x5f, 0x44, 0xf8, 0x88, 0xd9, 0x7d, 0xd3, 0xf1, 0x5f, 0xc4, 0x2b, 0x44, 0x15, 0x57, 0x31, 0xa4, 0xa1, 0xdb, 0x6d, 0x2a, 0x5a, 0x5a, 0xf7, 0xde, 0xd5, 0x23, 0x38, 0x00, 0xe5, 0x5c, 0x55, 0xe7, 0x37, 0x9c, 0xcb, 0x8b, 0xc0, 0x33, 0x42, 0x68, 0x23, 0x84, 0x7d, 0x89, 0x9d, 0xae, 0x59, 0x18, 0xae, 0xea, 0x46, 0x3f, 0xac, 0x57, 0x0d, 0x5d, 0x49, 0x14, 0x50, 0xe5, 0x70, 0x17, 0x73, 0x09, 0x11, 0x93, 0x6b, 0x02, 0x22, 0xb7, 0x63, 0xc9, 0xe6, 0xa4, 0xe3, 0xb1, 0xf7, 0xa6, 0x58, 0x8d, 0x14, 0xa1, 0xda, 0x6a, 0xb9, 0x38, 0xf9, 0x20, 0x45, 0x8c, 0xe6, 0x32, 0x23, 0x9d, 0x5f, 0xba, 0xcb, 0xb4, 0x95, 0xf9, 0xa9, 0x5c, 0x60, 0x03, 0x5a, 0x8c, 0xa7, 0xb9, 0x65, 0xa8, 0x84, 0x38, 0xc0, 0x25, 0xe6, 0xa7, 0xc0, 0x3b, 0xbc, 0x11, 0xed, 0x0e, 0x9a, 0x6f, 0xfe, 0x61, 0x79, 0x86, 0x92, 0x3a, 0xce, 0xe0, 0xb7, 0x70, 0xad, 0xe0, 0xcc, 0x88, 0x47, 0xd9, 0x2a, 0x3d, 0x41, 0x06, 0x77, 0x41, 0xbe, 0x3f, 0x55, 0x31, 0x54, 0x10, 0x14, 0x5b, 0xdf, 0x88, 0xb2, 0x9f, 0xff, 0x11, 0xb8, 0x11, 0xdc, 0x5e, 0x64, 0xf9, 0x97, 0x8a, 0x26, 0x6a, 0x44, 0xb4, 0x83, 0x83, 0x9b, 0x81, 0xaa, 0xfd, 0xb5, 0x8b, 0x16, 0x18, 0x2e, 0x5c, 0xe4, 0x5b, 0x8f, 0xdd, 0x7c, 0x1f, 0x33, 0x2f, 0xef, 0x57, 0x8c, 0x6a, 0x3f, 0x3c, 0x19, 0x5e, 0x73, 0x64, 0xc5, 0xaf, 0x1d, 0xa1, 0xb4, 0x11, 0xee, 0x6b, 0x7e, 0x66, 0xfb, 0xaa, 0x03, 0x17, 0xe4, 0xc9, 0x90, 0x4b, 0xf2, 0x50, 0x55, 0x71, 0xad, 0x31, 0x71, 0x49, 0xd7, 0x80, 0xd1, 0xa5, 0x9f, 0x6d, 0x71, 0x28, 0x2b, 0x65, 0xcf, 0x8d, 0xb1, 0x2a, 0x33, 0xdc, 0x93, 0xff, 0x86, 0xd7, 0xa6, 0xd0, 0x46, 0x66, 0x32, 0x3d, 0x18, 0x8c, 0xd3, 0xda, 0xf6, 0x1b, 0xa0, 0x2d, 0x29, 0xfd, 0x8d, 0x57, 0x2c, 0x82, 0xed, 0x38, 0x4a, 0x6f, 0xc4, 0x3c, 0x9a, 0x61, 0xcb, 0xe5, 0xcf, 0xd3, 0x83, 0xa1, 0x91, 0x93, 0x0d, 0x75, 0xfd, 0x4e, 0x2c, 0x83, 0xa0, 0x85, 0x27, 0x13, 0x5a, 0x24, 0xbd, 0x08, 0x1e, 0xe9, 0xab, 0x92, 0x41, 0xc2, 0x3a, 0xa0, 0xe1, 0xfd, 0x00, 0xb9, 0xf8, 0xca, 0x0b, 0x1a, 0x8e, 0xf6, 0x27, 0x9f, 0x5a, 0xf0, 0x23, 0x07, 0xc8, 0xbf, 0xf6, 0x74, 0xe7, 0xf8, 0x67, 0xfc, 0x28, 0x4e, 0x6a, 0x6c, 0xc6, 0x83, 0xe3, 0xf0, 0x01, 0xe0, 0x0f, 0x2d, 0xdf, 0x9e, 0x4b, 0x8b, 0x06, 0x15, 0x4c, 0x9f, 0xdf, 0x55, 0x14, 0x44, 0xde, 0x34, 0x35, 0x5a, 0xcb, 0xe5, 0xa7, 0xb5, 0x7e, 0x00, 0x31, 0x98, 0x5f, 0x51, 0x11, 0x37, 0xe1, 0xd2, 0x99, 0x8f, 0x70, 0x13, 0x40, 0xa0, 0xbe, 0xf8, 0xde, 0xac, 0x37, 0x06, 0xb6, 0x26, 0xf3, 0xb1, 0x97, 0x0b, 0x85, 0x68, 0x09, 0xa4, 0xc8, 0x34, 0x0a, 0x41, 0x6e, 0xac, 0x1a, 0x5b, 0xe0, 0x91, 0x6f, 0xa3, 0x0a, 0xf6, 0x05, 0x37, 0x32, 0xe1, 0x8e, 0xd8, 0xed, 0x55, 0xa3, 0x54, 0x3f, 0x62, 0x95, 0x82, 0xcf, 0x0a, 0x19, 0xb4, 0x9f, 0x04, 0xcc, 0x86, 0x7e, 0xf1, 0xe5, 0x8b, 0x67, 0x73, 0xa2, 0x46, 0x4e, 0xf2, 0x98, 0x94, 0xb5, 0xeb, 0xa5, 0xbd, 0xcb, 0x66, 0x82, 0xe9, 0x87, 0xe9, 0xe3, 0x50, 0x55, 0x4b, 0xd6, 0x67, 0x30, 0xe1, 0x7c, 0x15, 0x77, 0x29, 0xfd, 0x85, 0x67, 0x5a, 0xc4, 0xd5, 0x69, 0xfa, 0xc7, 0x66, 0x66, 0x49, 0xf7, 0x5a, 0xcd, 0xd1, 0x81, 0x5c, 0x74, 0x8d, 0xbf, 0xc5, 0xc2, 0xff, 0x4d, 0x90, 0xe8, 0x8e, 0x05, 0x00, 0xff, 0x7a, 0xd7, 0xb2, 0x7a, 0xad, 0x8b, 0xd6, 0x4b, 0x52, 0x09, 0x50, 0x4b
    };

    SrsHandshakeBytes bytes;
    if (true) {
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c0c1());
        memcpy(bytes.c0c1, c0c1, 1537);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_s0s1s2());
        memcpy(bytes.s0s1s2, s0s1s2, 3073);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c2());
        memcpy(bytes.c2, c2, 1536);
    }
    
    SrsHandshakeBytes* hs_bytes = &bytes;
    if (true) {
        bool is_valid;
        
        c1s1 c1;
        ASSERT_EQ(ERROR_SUCCESS, c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0));
        ASSERT_EQ(ERROR_SUCCESS, c1.c1_validate_digest(is_valid));
        ASSERT_TRUE(is_valid);
        
        c1s1 s1;
        ASSERT_EQ(ERROR_SUCCESS, s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema()));
        ASSERT_EQ(ERROR_SUCCESS, s1.s1_validate_digest(is_valid));
        ASSERT_TRUE(is_valid);
        
        c2s2 c2;
        c2.parse(hs_bytes->c2, 1536);
        ASSERT_EQ(ERROR_SUCCESS, c2.c2_validate(&s1, is_valid));
        ASSERT_TRUE(is_valid);
        
        c2s2 s2;
        s2.parse(hs_bytes->s0s1s2 + 1 + 1536, 1536);
        ASSERT_EQ(ERROR_SUCCESS, s2.s2_validate(&c1, is_valid));
        ASSERT_TRUE(is_valid);
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.complex_handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(c0c1, 1537);
        io.append(c2, 1536);

        SrsRtmpServer r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }
}

VOID TEST(ProtocolHandshakeTest, SimpleHandshake)
{
    srs_error_t err;

    uint8_t c0c1[] = {
        0x03, 0x01, 0x14, 0xf7, 0x4e, 0x80, 0x00, 0x07, 0x02, 0xac, 0x14, 0x98, 0x57, 0x0a, 0x07, 0x58, 0x44, 0x96, 0x47, 0xb5, 0x9a, 0x73, 0xf6, 0x07, 0x0f, 0x49, 0x0d, 0x72, 0xb8, 0x16, 0xbb, 0xb2, 0xb7, 0x61, 0x17, 0x79, 0xa0, 0xe9, 0x98, 0xca, 0xb2, 0x86, 0x64, 0x5f, 0x65, 0x3e, 0xfc, 0x4d, 0xc0, 0x0e, 0x4c, 0xfa, 0x91, 0xc7, 0x0f, 0x2e, 0x57, 0x31, 0x4b, 0x96, 0xef, 0xc9, 0x81, 0x02, 0x00, 0x54, 0x25, 0x2b, 0xb2, 0x0d, 0x7c, 0xee, 0xba, 0xdb, 0xe4, 0x06, 0x78, 0xcd, 0x70, 0x2c, 0x54, 0x5a, 0x3a, 0x03, 0x13, 0x2e, 0xe7, 0x4b, 0x87, 0x40, 0x77, 0x0b, 0x9f, 0xd2, 0xab, 0x32, 0x07, 0x6f, 0x1e, 0x75, 0x74, 0xe9, 0xc7, 0x44, 0xd9, 0x76, 0x53, 0xba, 0xe2, 0x52, 0xfa, 0xcc, 0xef, 0x34, 0xd5, 0x14, 0x61, 0xac, 0xcc, 0x63, 0xfd, 0x2b, 0x2d, 0xb3, 0xb8, 0xdd, 0x8a, 0x51, 0x9a, 0x2d, 0x0e, 0xfa, 0x84, 0x25, 0x55, 0xb2, 0xb7, 0x94, 0x54, 0x68, 0xfb, 0x94, 0xdf, 0xd8, 0xeb, 0x43, 0xd0, 0x11, 0x70, 0x8f, 0xf5, 0x48, 0xfc, 0x69, 0x4d, 0x5b, 0xc6, 0x53, 0x8a, 0x22, 0xea, 0x62, 0x84, 0x89, 0x6b, 0xfe, 0x4e, 0xab, 0x51, 0x98, 0xf4, 0x4f, 0xae, 0xf8, 0xdf, 0xac, 0x43, 0xed, 0x5a, 0x04, 0x97, 0xc4, 0xbe, 0x44, 0x5b, 0x99, 0x20, 0x68, 0x67, 0x0f, 0xe3, 0xfa, 0x4c, 0x9d, 0xe7, 0x0b, 0x3f, 0x80, 0x7c, 0x4c, 0x35, 0xf6, 0xdd, 0x20, 0x05, 0xfd, 0x0f, 0x39, 0xb7, 0x36, 0x45, 0x4c, 0xb7, 0x62, 0x92, 0x35, 0x2a, 0xcd, 0xb9, 0x49, 0xea, 0x12, 0x0b, 0x5f, 0x39, 0xae, 0x3b, 0x49, 0x29, 0xe6, 0x30, 0xc7, 0x7c, 0x77, 0xaf, 0x00, 0x43, 0x4d, 0x06, 0x45, 0x72, 0x73, 0x25, 0x71, 0x5e, 0x35, 0x04, 0xbd, 0xe9, 0x48, 0x23, 0x64, 0x4d, 0x15, 0x0b, 0xc5, 0x3f, 0x6e, 0x3a, 0xd5, 0xd5, 0xa6, 0xae, 0x3b, 0x4c, 0x66, 0x6a, 0x70, 0x8b, 0xf3, 0x6a, 0x43, 0xc4, 0xb9, 0xbd, 0xa0, 0x09, 0x72, 0xbc, 0xce, 0x7a, 0xea, 0x49, 0xf2, 0x86, 0xa7, 0xd8, 0x4a, 0x87, 0x28, 0xca, 0x2c, 0x53, 0xee, 0x96, 0x0b, 0xbe, 0x15, 0x14, 0xa8, 0x00, 0xca, 0x76, 0x08, 0x4d, 0x0f, 0xef, 0x78, 0x4b, 0xf6, 0x47, 0x60, 0xfc, 0x16, 0x00, 0x7c, 0x6b, 0x49, 0x39, 0x64, 0x36, 0xee, 0x45, 0x3a, 0x9a, 0xa5, 0xbf, 0xfb, 0x7b, 0xe7, 0xcf, 0x42, 0x82, 0x48, 0x1b, 0x30, 0xfe, 0x0d, 0xba, 0x10, 0xb8, 0xe1, 0x40, 0xcc, 0x6f, 0x36, 0x1c, 0x94, 0x5d, 0x50, 0x9e, 0x21, 0x08, 0xc9, 0xd5, 0xb0, 0x32, 0x51, 0x6a, 0x8f, 0xfa, 0x57, 0x8d, 0x45, 0xd7, 0xd2, 0xd0, 0xd6, 0x6c, 0x78, 0x95, 0xe9, 0xe1, 0x20, 0x97, 0x1a, 0x43, 0x40, 0xa3, 0xb5, 0xcc, 0x4b, 0x12, 0x84, 0x1e, 0x0e, 0xd3, 0x32, 0xca, 0x99, 0xc3, 0x2b, 0x78, 0x17, 0x24, 0x6b, 0xc7, 0xbc, 0x9d, 0x05, 0xc6, 0xaf, 0x8f, 0x19, 0x75, 0x3c, 0x08, 0xa6, 0x08, 0x26, 0x5b, 0xf4, 0x10, 0x40, 0xaa, 0x6a, 0x7e, 0xb9, 0xde, 0x0b, 0x23, 0x3f, 0x53, 0x5a, 0x20, 0x13, 0x62, 0xec, 0x53, 0x86, 0x81, 0x1f, 0xf6, 0x8e, 0xe3, 0xd1, 0xaa, 0xb5, 0x41, 0x87, 0x62, 0xd2, 0xb7, 0x09, 0x12, 0x71, 0x01, 0x2c, 0xac, 0x6d, 0x9d, 0x37, 0x46, 0x5b, 0xdc, 0x76, 0x2c, 0x96, 0x61, 0x88, 0x55, 0x5a, 0x20, 0xc2, 0x84, 0x95, 0xbd, 0x72, 0xc4, 0xb7, 0x22, 0xae, 0xeb, 0x49, 0x0e, 0x16, 0xf1, 0xf1, 0xbf, 0xc5, 0xc7, 0xa8, 0x8d, 0xfb, 0xe1, 0x08, 0x6c, 0xc4, 0x79, 0x81, 0x13, 0xe8, 0x39, 0xbf, 0x6e, 0x5c, 0xa1, 0x62, 0xfb, 0x32, 0x2a, 0x62, 0xf0, 0x12, 0x07, 0x31, 0x93, 0x40, 0xf3, 0xc0, 0xea, 0x1d, 0xd8, 0x65, 0xba, 0x12, 0xb3, 0x9b, 0xf5, 0x59, 0x9c, 0x4e, 0xf6, 0xb9, 0xf7, 0x85, 0xa1, 0xd9, 0x2f, 0x7c, 0x8b, 0xd0, 0xfc, 0x53, 0x3b, 0xed, 0x85, 0xa4, 0xd2, 0x5e, 0x69, 0x61, 0x02, 0x53, 0xb6, 0x19, 0xc7, 0x82, 0xea, 0x8a, 0x45, 0x01, 0x5d, 0x4b, 0xb3, 0x06, 0x86, 0x7f, 0x4b, 0x2f, 0xe7, 0xa8, 0xd0, 0x28, 0x62, 0x02, 0xe8, 0xf3, 0x9e, 0x1e, 0x72, 0x82, 0x07, 0x9f, 0xdd, 0xd2, 0x83, 0x7d, 0x89, 0x73, 0x1b, 0x6f, 0x35, 0x20, 0xb7, 0x88, 0x15, 0x92, 0xa7, 0x11, 0xfe, 0x81, 0x68, 0xed, 0x14, 0x07, 0xdf, 0x4a, 0x06, 0x9c, 0x5e, 0x7e, 0x34, 0x3a, 0x2a, 0x8a, 0xd3, 0xe8, 0xf8, 0xd4, 0xdb, 0xe3, 0xe9, 0x73, 0xbf, 0xa7, 0xe9, 0x73, 0x62, 0xf2, 0x9d, 0xc1, 0xf7, 0x51, 0xeb, 0xff, 0xb7, 0xe6, 0xd9, 0xac, 0x46, 0x06, 0x74, 0xe2, 0x25, 0x3f, 0x46, 0x43, 0xce, 0x49, 0x52, 0x25, 0x1b, 0xf9, 0x24, 0x5c, 0xda, 0xfd, 0x7f, 0xf6, 0xef, 0xb3, 0xd5, 0xe9, 0x6e, 0x35, 0xb8, 0xd1, 0x0e, 0x2c, 0xc1, 0x48, 0x5a, 0x27, 0x0a, 0x81, 0x01, 0x0f, 0xe4, 0x51, 0xcf, 0x89, 0x36, 0xd3, 0xe8, 0x5e, 0x05, 0xb9, 0x83, 0x42, 0xf3, 0xa5, 0x94, 0x67, 0x6d, 0x6a, 0x6e, 0xad, 0xf8, 0x90, 0xb1, 0x1d, 0x63, 0x18, 0x52, 0xc1, 0xbf, 0xbc, 0xad, 0xf4, 0xd2, 0xc5, 0xef, 0xca, 0x4c, 0xfe, 0xa1, 0xda, 0x15, 0x92, 0x4c, 0x42, 0x3d, 0xfc, 0x80, 0x7e, 0x49, 0x13, 0x4e, 0xf6, 0xe1, 0xee, 0x70, 0xca, 0xd9, 0x0a, 0xde, 0x9b, 0xea, 0xcd, 0xf9, 0x90, 0xfd, 0xae, 0x09, 0xce, 0xb6, 0xa0, 0xf7, 0xd1, 0xe6, 0x0c, 0x55, 0x1e, 0x3f, 0xbb, 0x1e, 0xff, 0x3d, 0xdb, 0xdd, 0x27, 0x80, 0x06, 0x53, 0x7e, 0x0b, 0x2a, 0x80, 0x24, 0x51, 0x5c, 0x6a, 0xab, 0x32, 0x5d, 0x37, 0x8a, 0xf4, 0xb7, 0x11, 0xa7, 0xc1, 0x9e, 0x05, 0x2c, 0x16, 0xc2, 0x08, 0xe2, 0xac, 0x1a, 0xeb, 0x60, 0xf8, 0xd2, 0xea, 0x39, 0x01, 0x1c, 0x64, 0xbd, 0x22, 0x80, 0x19, 0x20, 0xc9, 0x6f, 0xdd, 0x5c, 0x73, 0x8c, 0xa1, 0x53, 0x48, 0x2e, 0x99, 0x1d, 0xc0, 0x8f, 0x28, 0xf1, 0xe3, 0xc5, 0xc5, 0x65, 0x53, 0xf2, 0x44, 0x44, 0x24, 0xb9, 0xe2, 0x73, 0xe4, 0x76, 0x14, 0x56, 0xb8, 0x82, 0xe3, 0xb4, 0xfd, 0x68, 0x31, 0xed, 0x40, 0x10, 0x99, 0xd3, 0x3d, 0xe5, 0x6b, 0x14, 0x61, 0x66, 0x9a, 0xf6, 0x33, 0x98, 0xc5, 0x4d, 0x11, 0xbb, 0xf8, 0x56, 0xf8, 0x8f, 0xd7, 0xb9, 0xda, 0xa3, 0x56, 0x1a, 0xe0, 0x9e, 0xbe, 0x5f, 0x56, 0xe5, 0xb9, 0xd8, 0xf3, 0xbc, 0x19, 0xf5, 0xe9, 0x1f, 0xd2, 0xea, 0xf4, 0x5a, 0xde, 0xed, 0xd4, 0x9e, 0xc8, 0xf5, 0x54,
        0x83, 0x8b, 0x8c, 0x2d, 0x24, 0x0e, 0x30, 0xb1, 0x84, 0xa2, 0xbe, 0x2c, 0x86, 0xe6, 0x42, 0x82, 0xaa, 0x37, 0x64, 0x55, 0x51, 0xbc, 0xde, 0xc0, 0x63, 0x88, 0xf6, 0x31, 0x71, 0x52, 0xd5, 0x34, 0x0f, 0x8e, 0xcb, 0x28, 0x65, 0x93, 0x1a, 0x66, 0x3b, 0x21, 0x00, 0xaa, 0x7a, 0xda, 0x2d, 0xf6, 0x7e, 0xb5, 0x27, 0x79, 0xf4, 0x50, 0x3b, 0x10, 0x6b, 0x3c, 0xd7, 0x99, 0x9d, 0xf6, 0xc5, 0x01, 0x91, 0xa0, 0xd5, 0x4f, 0xd3, 0x76, 0x54, 0xa8, 0x5c, 0x35, 0x1d, 0xe2, 0x35, 0x6a, 0x68, 0x67, 0x03, 0xc4, 0x1f, 0xe9, 0x60, 0xb8, 0x49, 0xb1, 0x9a, 0x40, 0xd9, 0x3c, 0x4c, 0x73, 0xaa, 0x88, 0x63, 0xaf, 0xfe, 0xe8, 0xa8, 0x0c, 0x96, 0xbe, 0xb4, 0x65, 0x7c, 0x27, 0xfb, 0xc1, 0x27, 0x24, 0x58, 0xab, 0x4b, 0xa0, 0x5a, 0x7d, 0xc7, 0xca, 0x2d, 0xa5, 0x22, 0xa7, 0xed, 0x26, 0x87, 0xd5, 0x44, 0x1a, 0xc7, 0xdd, 0xfb, 0x60, 0xfc, 0xe5, 0x50, 0xd9, 0x8d, 0xa7, 0xdb, 0x78, 0xb6, 0x9d, 0x80, 0x0f, 0xb9, 0x5f, 0xa7, 0x53, 0x92, 0x5d, 0x18, 0xce, 0x89, 0xc2, 0x69, 0xee, 0xcf, 0xb6, 0x66, 0xe5, 0x66, 0xd2, 0xe3, 0x35, 0x74, 0x0b, 0x83, 0xb6, 0xde, 0xf1, 0xfb, 0xb4, 0x1d, 0x4b, 0x94, 0x95, 0x06, 0x82, 0xe7, 0x1c, 0xf8, 0xc5, 0xe6, 0xd0, 0xf2, 0x17, 0x37, 0x44, 0xfe, 0x99, 0x43, 0x82, 0xbb, 0x88, 0xe4, 0x43, 0x67, 0xcc, 0x4d, 0x5f, 0xa6, 0x26, 0xd7, 0x53, 0xd6, 0x45, 0x96, 0x2b, 0x63, 0xd1, 0x2a, 0xa1, 0x2c, 0x41, 0x59, 0x8b, 0xb8, 0xc1, 0x89, 0x03, 0x3a, 0x61, 0x13, 0xc4, 0x2c, 0x37, 0xa5, 0xbf, 0xd7, 0xdb, 0xd8, 0x53, 0x5f, 0xa1, 0xdb, 0xdb, 0xa5, 0x73, 0xb6, 0xf7, 0x74, 0xa0, 0xf8, 0x93, 0xf5, 0x61, 0xee, 0x3c, 0xe7, 0x00, 0x01, 0x98, 0xe0, 0xa1, 0x22, 0xb6, 0x9a, 0x83, 0x44, 0xa1, 0xe6, 0x70, 0x56, 0x65, 0x92, 0x1e, 0xf0, 0xbc, 0x73, 0xa5, 0x7a, 0xc1, 0x1a, 0x02, 0xf9, 0xd4, 0xc4, 0x7c, 0x81, 0xda, 0x15, 0xc0, 0xd4, 0x25, 0xdc, 0x17, 0xa6, 0x0d, 0x90, 0x55, 0xf2, 0x10, 0xf8, 0xa7, 0x71, 0x9b, 0xed, 0xdf, 0xdf, 0xa1, 0xe4, 0xb9, 0x12, 0x6b, 0x05, 0x3e, 0x83, 0x99, 0x49, 0xbf, 0x66, 0xbb, 0xf6, 0x76, 0xd3, 0xa9, 0x24, 0x61, 0x8c, 0x25, 0x49, 0xd0, 0xf7, 0x83, 0x44, 0xfb, 0x27, 0xe2, 0x7d, 0x69, 0x6d, 0x34, 0x67, 0xed, 0x39, 0x89, 0x02, 0xcb, 0x2f, 0x33, 0x3c, 0xcd, 0x12, 0x42, 0x8f, 0x86, 0x7d, 0xda, 0x3f, 0xd7, 0x26, 0x62, 0x9c, 0x1f, 0x2e, 0xa8, 0xc3, 0x85, 0xf1, 0x73, 0xe5, 0x2c, 0x11, 0xde, 0x98, 0xc8, 0xb0, 0x10, 0x17, 0x55, 0xf5, 0x32, 0x52, 0x67, 0xca, 0x64, 0x50, 0x28, 0x9a, 0x24, 0x92, 0xa1, 0x97, 0x57, 0x81, 0xaf, 0xca, 0x1e, 0xc0, 0xa4, 0x71, 0x2d, 0x2a, 0xec, 0xc9, 0x23, 0x6a, 0x0c, 0x1d, 0x54, 0x15, 0x2a, 0x56, 0x42, 0x0a, 0x83, 0xff, 0x28, 0xba, 0xe7, 0x68, 0x38, 0xf5, 0x32, 0xa9, 0xb7, 0xe7, 0x70, 0x32, 0xa8, 0x79, 0x5e, 0x46, 0x1d, 0xec, 0x29, 0x8a, 0xde, 0x41, 0x94, 0x94, 0x26, 0x79, 0xc2, 0x52, 0x23, 0xe0, 0xa1, 0x1d, 0x65, 0x0c, 0xbe, 0x1b, 0x87, 0x2a, 0x21, 0x53, 0x2f, 0x35, 0x56, 0xe8, 0xd1, 0x7b, 0xb8, 0x23, 0x75, 0x56, 0xc7, 0x08, 0x9d, 0x13, 0xf0, 0x8f, 0x80, 0x38, 0xe9, 0x92, 0xf7, 0x16, 0xc2, 0xf3, 0x74, 0xa7, 0x92, 0xf5, 0x49, 0x7d, 0x09, 0x41, 0xbc, 0x07, 0x61, 0x1f, 0xe6, 0xa0, 0xd8, 0xa6, 0xe3, 0x72, 0xa4, 0x59, 0x4a, 0xd9, 0x33, 0x40, 0x80, 0x3a, 0x3a, 0xb3, 0xa0, 0x96, 0xca, 0x56, 0x98, 0xbd, 0x1f, 0x80, 0x86, 0x6c, 0xe1, 0x09, 0x64, 0x1b, 0x1a, 0xc9, 0x52, 0xaa, 0xd1, 0x39, 0xea, 0x4b, 0x6a, 0x3e, 0x4e, 0xa4, 0xea, 0x00, 0xde, 0x07, 0x0b, 0x23, 0xbc, 0x40, 0xc4, 0xd2, 0xd9, 0xf6, 0xda, 0x8e, 0x22, 0x36, 0xbe, 0x5e, 0x65, 0x6e, 0xbe, 0xc8, 0xb0, 0x07, 0xa2, 0x2d, 0xe9, 0x4b, 0x73, 0x54, 0xe6, 0x0a, 0xf2, 0xd3, 0x83, 0x8b, 0x27, 0x4c, 0xcc, 0x0c, 0x8a, 0xd4, 0x2b, 0xb8, 0x95, 0x2e, 0x42, 0x64, 0x29, 0xc1, 0xe0, 0x6b, 0x92, 0xab, 0xfe, 0x53, 0x06, 0x96, 0x4a, 0x8c, 0x5d, 0x7c, 0x51, 0x74, 0xd0, 0x1e, 0x37, 0x35, 0x9c, 0x1e, 0x69, 0x8f, 0x68, 0x18, 0xd9, 0xbe, 0xaf, 0x81, 0x9b, 0x7e, 0xd8, 0x71, 0x9d, 0xb6, 0x50, 0x43, 0x78, 0x85, 0x7d, 0x65, 0x93, 0x45, 0xb4, 0x02, 0xd0, 0x5c, 0x36, 0xe2, 0x62, 0x3f, 0x40, 0x33, 0xee, 0x91, 0xe5, 0x3f, 0x67, 0x39, 0x2f, 0x1b, 0x89, 0x9f, 0x04, 0x9d, 0x46, 0x3e, 0x70, 0x92, 0x9e, 0x8c, 0xf5
    };
    uint8_t s0s1s2[] = {
        0x03, 0xac, 0x44, 0x29, 0x53, 0x04, 0x05, 0x00, 0x01, 0x6e, 0x65, 0x69, 0x2d, 0x69, 0x2d, 0x69, 0x73, 0x6e, 0x69, 0x73, 0x6c, 0x65, 0x72, 0x69, 0x72, 0x76, 0x65, 0x72, 0x69, 0x77, 0x74, 0x2e, 0x6e, 0x72, 0x76, 0x72, 0x65, 0x72, 0x70, 0x72, 0x69, 0x69, 0x70, 0x72, 0x73, 0x6e, 0x65, 0x72, 0x72, 0x6e, 0x2d, 0x65, 0x74, 0x72, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x40, 0x69, 0x69, 0x76, 0x77, 0x2d, 0x73, 0x65, 0x72, 0x72, 0x76, 0x73, 0x72, 0x2e, 0x2d, 0x76, 0x65, 0x31, 0x65, 0x6d, 0x6d, 0x73, 0x69, 0x73, 0x74, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x72, 0x65, 0x2d, 0x74, 0x69, 0x31, 0x65, 0x2d, 0x6f, 0x77, 0x2e, 0x76, 0x77, 0x2d, 0x77, 0x72, 0x65, 0x65, 0x31, 0x74, 0x73, 0x70, 0x74, 0x6e, 0x72, 0x6e, 0x73, 0x6d, 0x2e, 0x69, 0x72, 0x2d, 0x65, 0x69, 0x77, 0x69, 0x76, 0x72, 0x77, 0x72, 0x32, 0x6e, 0x65, 0x6c, 0x2e, 0x2d, 0x6e, 0x69, 0x6d, 0x6c, 0x73, 0x65, 0x73, 0x70, 0x2d, 0x65, 0x72, 0x40, 0x72, 0x74, 0x6e, 0x6e, 0x6d, 0x6f, 0x70, 0x74, 0x73, 0x2d, 0x63, 0x69, 0x32, 0x31, 0x2d, 0x40, 0x69, 0x70, 0x2d, 0x2d, 0x72, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x69, 0x65, 0x6e, 0x32, 0x6f, 0x6c, 0x6e, 0x72, 0x73, 0x77, 0x65, 0x65, 0x72, 0x32, 0x6d, 0x65, 0x6c, 0x2d, 0x72, 0x6e, 0x65, 0x6d, 0x31, 0x65, 0x74, 0x2d, 0x6f, 0x72, 0x65, 0x63, 0x69, 0x40, 0x70, 0x2d, 0x65, 0x6d, 0x2d, 0x77, 0x63, 0x63, 0x74, 0x40, 0x36, 0x2d, 0x72, 0x65, 0x70, 0x2d, 0x6e, 0x69, 0x6d, 0x65, 0x74, 0x70, 0x76, 0x40, 0x76, 0x72, 0x72, 0x69, 0x77, 0x76, 0x69, 0x74, 0x74, 0x65, 0x31, 0x6d, 0x2e, 0x6f, 0x72, 0x73, 0x73, 0x6c, 0x40, 0x36, 0x72, 0x70, 0x72, 0x70, 0x72, 0x69, 0x32, 0x6c, 0x77, 0x70, 0x76, 0x65, 0x72, 0x76, 0x63, 0x65, 0x65, 0x77, 0x72, 0x6e, 0x2e, 0x76, 0x69, 0x69, 0x2e, 0x40, 0x72, 0x2e, 0x2e, 0x72, 0x73, 0x6e, 0x72, 0x72, 0x6e, 0x70, 0x40, 0x77, 0x65, 0x77, 0x65, 0x70, 0x63, 0x74, 0x2d, 0x70, 0x72, 0x2d, 0x74, 0x72, 0x31, 0x65, 0x6e, 0x2d, 0x76, 0x2d, 0x2d, 0x2d, 0x74, 0x76, 0x2d, 0x74, 0x65, 0x2e, 0x2d, 0x6c, 0x76, 0x2d, 0x6c, 0x70, 0x73, 0x6d, 0x65, 0x72, 0x31, 0x31, 0x36, 0x76, 0x73, 0x73, 0x6e, 0x2d, 0x6e, 0x73, 0x72, 0x2d, 0x6f, 0x6c, 0x65, 0x74, 0x77, 0x65, 0x69, 0x72, 0x69, 0x65, 0x6d, 0x76, 0x31, 0x65, 0x73, 0x72, 0x6c, 0x72, 0x77, 0x65, 0x76, 0x74, 0x72, 0x69, 0x72, 0x76, 0x32, 0x73, 0x6d, 0x72, 0x2d, 0x6d, 0x40, 0x69, 0x40, 0x69, 0x31, 0x69, 0x6f, 0x6e, 0x6d, 0x69, 0x73, 0x70, 0x72, 0x77, 0x6f, 0x6f, 0x65, 0x77, 0x76, 0x70, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x32, 0x36, 0x6c, 0x74, 0x6e, 0x72, 0x74, 0x2d, 0x6e, 0x6c, 0x72, 0x72, 0x2d, 0x74, 0x65, 0x73, 0x70, 0x65, 0x72, 0x6c, 0x65, 0x65, 0x2d, 0x6e, 0x70, 0x6e, 0x40, 0x65, 0x6e, 0x6e, 0x74, 0x65, 0x6e, 0x72, 0x6e, 0xfe, 0x5a, 0x38, 0x79, 0x81, 0xe8, 0x49, 0xee, 0x93, 0xbb, 0xa0, 0x59, 0x4a, 0xa0, 0xcc, 0x31, 0xbf, 0x0d, 0x86, 0xc0, 0x3f, 0xae, 0x2a, 0x16, 0xfa, 0xf0, 0x4e, 0x0f, 0xa3, 0x01, 0x06, 0xa0, 0x0e, 0xa5, 0x8c, 0xa4, 0xca, 0xd2, 0x01, 0xa5, 0x90, 0xbd, 0x55, 0xd1, 0x42, 0x2b, 0xd4, 0xb3, 0xbb, 0x06, 0xb1, 0x3a, 0x94, 0x41, 0x76, 0x1d, 0xa5, 0x23, 0x6e, 0x1e, 0x59, 0x73, 0x63, 0x34, 0x60, 0xd3, 0x48, 0xc0, 0x3b, 0xcf, 0xf1, 0xa8, 0x38, 0xd6, 0xf3, 0x5e, 0x6d, 0xcb, 0xea, 0xfc, 0x9c, 0x52, 0xae, 0x9a, 0x89, 0xdb, 0x24, 0x1b, 0x92, 0x4a, 0x85, 0x97, 0x3c, 0xd8, 0x4c, 0x31, 0xad, 0xfd, 0x00, 0xef, 0xc5, 0x17, 0xa5, 0x22, 0xc0, 0xf1, 0x94, 0x18, 0xec, 0xf6, 0x49, 0xe5, 0x05, 0x11, 0x12, 0x67, 0x6c, 0x71, 0xc0, 0x84, 0x6d, 0x50, 0xf8, 0x23, 0x01, 0x57, 0xc4, 0xfc, 0x73, 0x65, 0x69, 0x6e, 0x65, 0x72, 0x6d, 0x6f, 0x69, 0x2d, 0x65, 0x65, 0x69, 0x63, 0x63, 0x69, 0x2d, 0x72, 0x2d, 0x69, 0x2d, 0x2d, 0x77, 0x72, 0x76, 0x72, 0x72, 0x2d, 0x76, 0x70, 0x63, 0x69, 0x74, 0x73, 0x6d, 0x65, 0x6c, 0x2d, 0x73, 0x6c, 0x65, 0x6e, 0x73, 0x77, 0x69, 0x63, 0x69, 0x70, 0x31, 0x40, 0x72, 0x69, 0x2d, 0x2d, 0x2d, 0x72, 0x72, 0x6c, 0x72, 0x63, 0x72, 0x77, 0x6e, 0x6c, 0x2d, 0x72, 0x2e, 0x76, 0x72, 0x65, 0x6d, 0x76, 0x36, 0x6d, 0x72, 0x77, 0x72, 0x65, 0x65, 0x69, 0x72, 0x76, 0x6d, 0x76, 0x74, 0x76, 0x72, 0x65, 0x69, 0x72, 0x6e, 0x6d, 0x77, 0x6c, 0x40, 0x32, 0x70, 0x65, 0x65, 0x69, 0x72, 0x31, 0x2e, 0x70, 0x36, 0x31, 0x65, 0x70, 0x72, 0x72, 0x73, 0x72, 0x6e, 0x6e, 0x73, 0x32, 0x2d, 0x2d, 0x2d, 0x69, 0x65, 0x31, 0x74, 0x6e, 0x65, 0x74, 0x65, 0x76, 0x69, 0x6d, 0x6c, 0x6e, 0x70, 0x74, 0x73, 0x72, 0x6d, 0x72, 0x72, 0x69, 0x65, 0x74, 0x65, 0x65, 0x2d, 0x70, 0x74, 0x6e, 0x74, 0x65, 0x6f, 0x72, 0x69, 0x76, 0x40, 0x31, 0x69, 0x72, 0x6d, 0x6d, 0x77, 0x69, 0x72, 0x65, 0x6e, 0x40, 0x63, 0x40, 0x65, 0x65, 0x69, 0x2d, 0x72, 0x65, 0x40, 0x69, 0x32, 0x74, 0x73, 0x6e, 0x36, 0x2d, 0x70, 0x65, 0x6c, 0x70, 0x6e, 0x72, 0x69, 0x32, 0x65, 0x74, 0x76, 0x77, 0x73, 0x6f, 0x77, 0x65, 0x72, 0x2d, 0x6e, 0x73, 0x65, 0x65, 0x70, 0x65, 0x2d, 0x65, 0x73, 0x2d, 0x65, 0x2e, 0x73, 0x69, 0x67, 0x45, 0x8b, 0x6b, 0x3b, 0xc9, 0x5f, 0x09, 0x65, 0x65, 0x72, 0x6c, 0x73, 0x6d, 0x70, 0x70, 0x73, 0x63, 0x70, 0x40, 0x72, 0x76, 0x65, 0x6e, 0x6f, 0x6c, 0x69, 0x2e, 0x72, 0x73, 0x76, 0x69, 0x77, 0x72, 0x2d, 0x69, 0x6e, 0x69, 0x65, 0x77, 0x73, 0x69, 0x70, 0x77, 0x63, 0x65, 0x74, 0x72, 0x73, 0x31, 0x65, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x6d, 0x65, 0x36, 0x6e, 0x6e, 0x69, 0x6d, 0x6e, 0x70, 0x77, 0x72, 0x65, 0x31, 0x65, 0x6e, 0x6e, 0x65, 0x2d, 0x65, 0x65, 0x2e, 0x77, 0x6e, 0x6f, 0x2d, 0x76, 0x65, 0x72, 0x6c, 0x31, 0x76, 0x65, 0x72, 0x2d, 0x36, 0x6c, 0x70, 0x6f, 0x65, 0x72, 0x73, 0x63, 0x72, 0x77, 0x73, 0x72, 0x65, 0x65, 0x65, 0x6c, 0x76, 0x72, 0x65, 0x6e, 0x65, 0x2e, 0x6f, 0x2d, 0x72, 0x70, 0x65, 0x74, 0x72,
        0x77, 0x69, 0x69, 0x72, 0x65, 0x77, 0x6c, 0x72, 0x2d, 0x69, 0x72, 0x31, 0x6e, 0x65, 0x70, 0x72, 0x74, 0x76, 0x6c, 0x2e, 0x72, 0x65, 0x72, 0x6c, 0x73, 0x6c, 0x2e, 0x2e, 0x72, 0x2d, 0x6e, 0x63, 0x32, 0x2e, 0x65, 0x2d, 0x65, 0x69, 0x2d, 0x65, 0x70, 0x6e, 0x72, 0x72, 0x32, 0x2e, 0x73, 0x70, 0x77, 0x65, 0x73, 0x77, 0x73, 0x40, 0x40, 0x73, 0x63, 0x2e, 0x65, 0x76, 0x70, 0x65, 0x69, 0x65, 0x70, 0x73, 0x40, 0x65, 0x73, 0x2d, 0x2d, 0x2e, 0x2e, 0x73, 0x65, 0x6f, 0x65, 0x65, 0x6d, 0x76, 0x70, 0x6d, 0x69, 0x70, 0x70, 0x69, 0x2e, 0x76, 0x6e, 0x72, 0x72, 0x72, 0x6d, 0x73, 0x6f, 0x73, 0x72, 0x72, 0x72, 0x77, 0x70, 0x65, 0x69, 0x72, 0x73, 0x6e, 0x69, 0x65, 0x65, 0x74, 0x65, 0x69, 0x40, 0x63, 0x69, 0x70, 0x6c, 0x6e, 0x2d, 0x65, 0x69, 0x72, 0x63, 0x6c, 0x72, 0x2e, 0x36, 0x69, 0x72, 0x6c, 0x6c, 0x2d, 0x6f, 0x76, 0x69, 0x6f, 0x2d, 0x6d, 0x6c, 0x72, 0x72, 0x2e, 0x70, 0x73, 0x6d, 0x6f, 0x2e, 0x6e, 0x69, 0x65, 0x65, 0x2d, 0x6d, 0x76, 0x6e, 0x69, 0x73, 0x73, 0x73, 0x74, 0x63, 0x65, 0x76, 0x2e, 0x77, 0x2d, 0x36, 0x73, 0x69, 0x2d, 0x72, 0x72, 0x6c, 0x36, 0x74, 0x72, 0x6d, 0x65, 0x2d, 0x65, 0x2e, 0x6d, 0x31, 0x72, 0x6f, 0x74, 0x76, 0x31, 0x65, 0x6d, 0x69, 0x72, 0x69, 0x69, 0x2d, 0x72, 0x73, 0x72, 0x72, 0x76, 0x31, 0x6e, 0x2d, 0x69, 0x6e, 0x77, 0x70, 0x69, 0x72, 0x6e, 0x76, 0x74, 0x6f, 0x65, 0x63, 0x6f, 0x73, 0x65, 0x73, 0x72, 0x69, 0x69, 0x40, 0x6e, 0x65, 0x65, 0x65, 0x65, 0x77, 0x70, 0x70, 0x6e, 0x72, 0x6e, 0x65, 0x72, 0x32, 0x65, 0x2d, 0x77, 0x69, 0x6e, 0x70, 0x69, 0x6f, 0x76, 0x77, 0x72, 0x74, 0x77, 0x6e, 0x72, 0xfe, 0x98, 0xf3, 0xb4, 0xff, 0x3f, 0x2e, 0xdb, 0x59, 0xbd, 0x32, 0x02, 0x6a, 0x44, 0x03, 0x67, 0x9e, 0xe1, 0x98, 0x97, 0xed, 0x67, 0x6d, 0xb0, 0x8f, 0xa9, 0xb6, 0xf8, 0x4d, 0x92, 0x35, 0x19, 0x72, 0x72, 0x65, 0x74, 0x73, 0x6e, 0x65, 0x65, 0x69, 0x36, 0x72, 0x73, 0x2d, 0x70, 0x2d, 0x2d, 0x69, 0x6e, 0x72, 0x65, 0x32, 0x72, 0x77, 0x72, 0x73, 0x77, 0x73, 0x70, 0x2d, 0x2d, 0x69, 0x6c, 0x70, 0x74, 0x65, 0x69, 0x72, 0x74, 0x6e, 0x76, 0x65, 0x76, 0x76, 0x69, 0x69, 0x65, 0x70, 0x6e, 0x73, 0x6e, 0x36, 0x76, 0x70, 0x76, 0x6c, 0x6c, 0x70, 0x6e, 0x6e, 0x74, 0x2e, 0x6f, 0x32, 0x74, 0x76, 0x74, 0x40, 0x72, 0x6e, 0x72, 0x74, 0x74, 0x2d, 0x6f, 0x72, 0x73, 0x32, 0x72, 0x32, 0x72, 0x70, 0x65, 0x65, 0x6e, 0x72, 0x70, 0x73, 0x72, 0x69, 0x74, 0x74, 0x6e, 0x72, 0x6c, 0x31, 0x74, 0x77, 0x31, 0x63, 0x63, 0x74, 0x69, 0x72, 0x69, 0x72, 0x70, 0x31, 0x74, 0x72, 0x76, 0x65, 0x72, 0x65, 0x6c, 0x76, 0x6d, 0x72, 0x6c, 0x69, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x69, 0x6c, 0x74, 0x6e, 0x65, 0x69, 0x77, 0x73, 0x70, 0x69, 0x72, 0x2d, 0x65, 0x74, 0x2e, 0x65, 0x65, 0x6d, 0x72, 0x31, 0x2d, 0x72, 0x36, 0x65, 0x2d, 0x69, 0x6d, 0x36, 0x6e, 0x72, 0x6d, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x6e, 0x31, 0x6e, 0x40, 0x72, 0x40, 0x6f, 0x73, 0x6d, 0x36, 0x2e, 0x72, 0x65, 0x36, 0x74, 0x77, 0x65, 0x65, 0x73, 0x36, 0x76, 0x6c, 0x6f, 0x2d, 0x36, 0x6d, 0x36, 0x70, 0x32, 0x74, 0x6d, 0x65, 0x6d, 0x69, 0x65, 0x65, 0x69, 0x76, 0x69, 0x74, 0x2d, 0x63, 0x2d, 0x6e, 0x32, 0x72, 0x63, 0x2d, 0x77, 0x72, 0x74, 0x72, 0x70, 0x6e, 0x76, 0x6f, 0x72, 0x40, 0x65, 0x65, 0x6d, 0x77, 0x2d, 0x2d, 0x74, 0x6e, 0x73, 0x76, 0x65, 0x69, 0x69, 0x72, 0x6f, 0x65, 0x70, 0x69, 0x6d, 0x76, 0x69, 0x65, 0x72, 0x2d, 0x74, 0x2d, 0x69, 0x65, 0x72, 0x69, 0x6f, 0x72, 0x72, 0x69, 0x76, 0x72, 0x77, 0x69, 0x2e, 0x77, 0x69, 0x70, 0x69, 0x6d, 0x36, 0x72, 0x76, 0x65, 0x76, 0x73, 0x6e, 0x72, 0x65, 0x2e, 0x76, 0x2d, 0x76, 0x6f, 0x2d, 0x65, 0x73, 0x72, 0x74, 0x36, 0x2d, 0x6f, 0x70, 0x73, 0x74, 0x74, 0x77, 0x6c, 0x2d, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x72, 0x32, 0x2d, 0x72, 0x69, 0x6d, 0x6e, 0x72, 0x6c, 0x6f, 0x65, 0x36, 0x31, 0x65, 0x65, 0x69, 0x73, 0x31, 0x74, 0x69, 0x69, 0x65, 0x40, 0x69, 0x6e, 0x2d, 0x63, 0x40, 0x31, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x72, 0x65, 0x65, 0x76, 0x65, 0x70, 0x72, 0x6c, 0x2d, 0x6e, 0x73, 0x69, 0x65, 0x65, 0x6e, 0x2e, 0x63, 0x6c, 0x72, 0x65, 0x2d, 0x2e, 0x6d, 0x72, 0x76, 0x70, 0x69, 0x6d, 0x40, 0x32, 0x77, 0x72, 0x6e, 0x72, 0x6c, 0x36, 0x72, 0x31, 0x2d, 0x73, 0x74, 0x2d, 0x69, 0x63, 0x40, 0x70, 0x32, 0x65, 0x31, 0x69, 0x69, 0x65, 0x72, 0x63, 0x74, 0x72, 0x74, 0x77, 0x6e, 0x69, 0x72, 0x65, 0x76, 0x65, 0x77, 0x69, 0x69, 0x73, 0x6e, 0x77, 0x77, 0x73, 0x6f, 0x69, 0x70, 0x73, 0x2d, 0x65, 0x65, 0x73, 0x65, 0x77, 0x2d, 0x73, 0x72, 0x6d, 0x65, 0x32, 0x6e, 0x73, 0x36, 0x65, 0x72, 0x77, 0x70, 0x65, 0x69, 0x2d, 0x2d, 0x74, 0x6f, 0x6f, 0x36, 0x63, 0x74, 0x72, 0x63, 0x77, 0x69, 0x2e, 0x31, 0x6c, 0x65, 0x77, 0x72, 0x65, 0x76, 0x74, 0x2d, 0x77, 0x2e, 0x76, 0x72, 0x6e, 0x36, 0x70, 0x69, 0x2e, 0x6e, 0x72, 0x77, 0x69, 0x65, 0x74, 0x2d, 0x6e, 0x63, 0x6e, 0x70, 0x2d, 0x6e, 0x2e, 0x6d, 0x69, 0x65, 0x63, 0x65, 0x2d, 0x76, 0x70, 0x73, 0x31, 0x73, 0x2d, 0x76, 0x6e, 0x6e, 0x6c, 0x2d, 0x6c, 0x2d, 0x65, 0x6e, 0x73, 0x6c, 0x65, 0x74, 0x70, 0x65, 0x2d, 0x6e, 0x77, 0x76, 0x40, 0x69, 0x40, 0x63, 0x6f, 0x72, 0x32, 0x6d, 0x31, 0x72, 0x36, 0x69, 0x73, 0x72, 0x70, 0x65, 0x2d, 0x6c, 0x2e, 0x72, 0x74, 0x74, 0x65, 0x69, 0x6f, 0x69, 0x2d, 0x2d, 0x65, 0x6f, 0x65, 0x74, 0x72, 0x69, 0x76, 0x72, 0x72, 0x65, 0x69, 0x76, 0x69, 0x69, 0x6e, 0x31, 0x65, 0x76, 0x72, 0x73, 0x77, 0x72, 0x2d, 0x69, 0x65, 0x69, 0x70, 0x65, 0x6e, 0x6e, 0x65, 0x65, 0x6e, 0x2d, 0x72, 0x76, 0x72, 0x6c, 0x2e, 0x70, 0x76, 0x6e, 0x69, 0x72, 0x70, 0x73, 0x2d, 0x69, 0x74, 0x76, 0x72, 0x70, 0x65, 0x63, 0x72, 0x70, 0x6e, 0x36, 0x6c, 0x74, 0x72, 0x72, 0x72, 0x73, 0x65, 0x40, 0x63, 0x6d, 0x63, 0x32, 0x65, 0x32, 0x69, 0x6e,
        0x77, 0x65, 0x74, 0x72, 0x77, 0x40, 0x69, 0x65, 0x70, 0x31, 0x36, 0x72, 0x73, 0x2d, 0x72, 0x72, 0x32, 0x72, 0x6c, 0x77, 0x6e, 0x6f, 0x77, 0x6c, 0x74, 0x72, 0x2d, 0x6e, 0x65, 0x70, 0x6c, 0x72, 0x6f, 0x69, 0x2d, 0x2d, 0x69, 0x36, 0x69, 0x69, 0x76, 0x69, 0x69, 0x6d, 0x72, 0x73, 0x6f, 0x6d, 0x74, 0x70, 0x76, 0x6d, 0x6d, 0x69, 0x72, 0x70, 0x70, 0x2d, 0x31, 0x63, 0x6c, 0x65, 0x65, 0x6e, 0x2d, 0x77, 0x74, 0x73, 0x6c, 0x72, 0x6e, 0x65, 0x65, 0x2d, 0x6c, 0x69, 0x2d, 0x6e, 0x74, 0x70, 0x72, 0x77, 0x77, 0x65, 0x65, 0x65, 0x2d, 0x76, 0x6e, 0x72, 0x69, 0x69, 0x73, 0x65, 0x74, 0x73, 0x76, 0x72, 0x72, 0x72, 0x69, 0x72, 0x73, 0x72, 0x6f, 0x2e, 0x77, 0x2d, 0x2d, 0x6c, 0x6e, 0x65, 0x65, 0x6d, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x40, 0x69, 0x32, 0x69, 0x32, 0x6e, 0x65, 0x32, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x36, 0x6e, 0x72, 0x32, 0x6e, 0x65, 0x69, 0x32, 0x6f, 0x70, 0x72, 0x72, 0x65, 0x72, 0x72, 0x69, 0x6e, 0x6d, 0x69, 0x70, 0x6c, 0x6c, 0x65, 0x31, 0x72, 0x72, 0x73, 0x72, 0x70, 0x73, 0x72, 0x65, 0x65, 0x6e, 0x76, 0x69, 0x6d, 0x65, 0x6c, 0x65, 0x31, 0x74, 0x74, 0x72, 0x63, 0x2e, 0x69, 0x65, 0x2d, 0x6d, 0x72, 0x70, 0x6e, 0x6c, 0x65, 0x31, 0x73, 0x73, 0x40, 0x74, 0x72, 0x73, 0x2e, 0x74, 0x6e, 0x77, 0x6c, 0x6f, 0x70, 0x77, 0x76, 0x73, 0x72, 0x69, 0x77, 0x69, 0x6e, 0x69, 0x2d, 0x72, 0x70, 0x70, 0x73, 0x2e, 0x76, 0x73, 0x65, 0x72, 0x72, 0x74, 0x2d, 0x72, 0x65, 0x76, 0x69, 0x77, 0x72, 0x65, 0x2d, 0x72, 0x69, 0x36, 0x77, 0x77, 0x77, 0x40, 0x2d, 0x6d, 0x69, 0x74, 0x72, 0x2d, 0x32, 0x6f, 0x76, 0x72, 0x2d, 0x2d, 0x65, 0x2e, 0x2e, 0x72, 0x6e, 0x32, 0x74, 0x6c, 0x6e, 0x6c, 0x2e, 0x6d, 0x2d, 0x6f, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x65, 0x69, 0x40, 0x69, 0x77, 0x65, 0x6c, 0x2d, 0x69, 0x69, 0x65, 0x72, 0x72, 0x32, 0x40, 0x73, 0x65, 0x36, 0x76, 0x73, 0x72, 0x69, 0x63, 0x77, 0x72, 0x6c, 0x72, 0x6e, 0x74, 0x2d, 0x65, 0x69, 0x72, 0x70, 0x6d, 0x65, 0x6c, 0x73, 0x65, 0x6c, 0x32, 0x2d, 0x73, 0x70, 0x2d, 0x31, 0x72, 0x74, 0x2e, 0x65, 0x74, 0x72, 0x74, 0x72, 0x70, 0x69, 0x40, 0x36, 0x2d, 0x74, 0x72, 0x6c, 0x2d, 0x6e, 0x72, 0x6e, 0x6d, 0x63, 0x76, 0x74, 0x6d, 0x70, 0x32, 0x70, 0x69, 0x69, 0x2d, 0x73, 0x72, 0x74, 0x65, 0x74, 0x74, 0x70, 0x2d, 0x31, 0x6c, 0x77, 0x65, 0x72, 0x70, 0x73, 0x36, 0x6c, 0x72, 0x72, 0x65, 0x65, 0x76, 0x69, 0x2e, 0x6e, 0x72, 0x72, 0x36, 0x65, 0x69, 0x72, 0x69, 0x40, 0x6c, 0x74, 0x6c, 0x72, 0x2d, 0x70, 0x74, 0x76, 0x74, 0x6f, 0x72, 0x31, 0x73, 0x70, 0x65, 0x74, 0x69, 0x6e, 0x69, 0x6c, 0x70, 0x72, 0x65, 0x70, 0x72, 0x73, 0x69, 0x2d, 0x6d, 0x63, 0x2d, 0x72, 0x2d, 0x36, 0x73, 0x6e, 0x2d, 0x6d, 0x69, 0x76, 0x76, 0x6d, 0x74, 0x72, 0x77, 0x74, 0x2e, 0x6d, 0x65, 0x2d, 0x65, 0x6d, 0x2e, 0x6c, 0x73, 0x6e, 0x6f, 0x76, 0x31, 0x74, 0x65, 0x65, 0x31, 0x69, 0x65, 0x32, 0x2d, 0x74, 0x2d, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x65, 0x6e, 0x2d, 0x69, 0x32, 0x72, 0x73, 0x74, 0x65, 0x65, 0x69, 0x73, 0x77, 0x77, 0x2e, 0x6e, 0x72, 0x65, 0x70, 0x76, 0x40, 0x77, 0x65, 0x2d, 0x70, 0x36, 0x2d, 0x74, 0x65, 0x2d, 0x69, 0x74, 0x76, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x73, 0x31, 0x36, 0x69, 0x31, 0x74, 0x76, 0x65, 0x77, 0x6c, 0x6e, 0x6c, 0x32, 0x6e, 0x70, 0x73, 0x69, 0x69, 0x65, 0x72, 0x2d, 0x6e, 0x2d, 0x65, 0x65, 0x6c, 0x32, 0x77, 0x72, 0x69, 0x70, 0x76, 0x32, 0x65, 0x6c, 0x36, 0x65, 0x69, 0x31, 0x6e, 0x72, 0x6c, 0x6d, 0x65, 0x65, 0x77, 0x6e, 0x2d, 0x32, 0x77, 0x69, 0x65, 0x6d, 0x74, 0x77, 0x40, 0x65, 0x6e, 0x77, 0x73, 0x65, 0x72, 0x6c, 0x40, 0x65, 0x65, 0x72, 0x72, 0x74, 0x6e, 0x6c, 0x6d, 0x73, 0x69, 0x76, 0x72, 0x31, 0x2d, 0x65, 0x36, 0x72, 0x2d, 0x70, 0x69, 0x6e, 0x63, 0x31, 0x2d, 0x69, 0x6e, 0x65, 0x2d, 0x65, 0x2e, 0x77, 0x2d, 0x72, 0x76, 0x63, 0x69, 0x2d, 0x6d, 0x70, 0x2d, 0x6c, 0x69, 0x63, 0x69, 0x77, 0x6e, 0x69, 0x77, 0x36, 0x72, 0x69, 0x72, 0x2e, 0x74, 0x72, 0x6e, 0x65, 0x6f, 0x73, 0x2d, 0x2e, 0x72, 0x63, 0x76, 0x74, 0x36, 0x65, 0x72, 0x65, 0x6d, 0x32, 0x72, 0x70, 0x40, 0x65, 0x74, 0x6e, 0x32, 0x70, 0x2d, 0x31, 0x40, 0x6c, 0x65, 0x6c, 0x76, 0x69, 0x69, 0x76, 0x76, 0x73, 0x31, 0x6e, 0x65, 0x74, 0x65, 0x6d, 0x69, 0x2d, 0x72, 0x74, 0x74, 0x6c, 0x31, 0x74, 0x6e, 0x6e, 0x65, 0x77, 0x36, 0x69, 0x69, 0x72, 0x6e, 0x2d, 0x2d, 0x2d, 0x72, 0x73, 0x76, 0x72, 0x72, 0x65, 0x72, 0x65, 0x72, 0x2d, 0x6c, 0x76, 0x77, 0x63, 0x77, 0x72, 0x6d, 0x72, 0x2e, 0x65, 0x73, 0x32, 0x72, 0x36, 0x77, 0x72, 0x72, 0x6d, 0x74, 0x2d, 0x72, 0x2e, 0x73, 0x73, 0x65, 0x77, 0x6e, 0x65, 0x69, 0x65, 0x2d, 0x65, 0x77, 0x6f, 0x74, 0x72, 0x32, 0x40, 0x6e, 0x72, 0x69, 0x6e, 0x32, 0x70, 0x73, 0x72, 0x40, 0x2d, 0x65, 0x69, 0x65, 0x77, 0x65, 0x70, 0x40, 0x36, 0x72, 0x6c, 0x6d, 0x73, 0x69, 0x72, 0x72, 0x74, 0x36, 0x6c, 0x76, 0x65, 0x76, 0x2d, 0x74, 0x6c, 0x72, 0x72, 0x74, 0x6e, 0x73, 0x74, 0x69, 0x72, 0x6d, 0x40, 0x2d, 0x6e, 0x70, 0x73, 0x2d, 0x6d, 0x72, 0x72, 0x70, 0x65, 0x65, 0x36, 0x6e, 0x77, 0x2d, 0x69, 0x2d, 0x32, 0x72, 0x6d, 0x72, 0x6c, 0x32, 0x6c, 0x73, 0x6d, 0x65, 0x36, 0x69, 0x69, 0x72, 0x77, 0x74, 0x6f, 0x72, 0x6d, 0x6d, 0x69, 0x65, 0x73, 0x63, 0x65, 0x74, 0x74, 0x72, 0x65, 0x72, 0x2e, 0x6e, 0x73, 0x65, 0x76, 0x6c, 0x76, 0x77, 0x72, 0x6e, 0x6c, 0x32, 0x2d, 0x73, 0x65, 0x73, 0x2e, 0x76, 0x72, 0x65, 0x2d, 0x72, 0x77, 0x2d, 0x77, 0x70, 0x65, 0x6c, 0x72, 0x6e, 0x2e, 0x31, 0x73, 0x2e, 0x72, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x6d, 0x32, 0x70, 0x76, 0x76, 0x31, 0x76, 0x77, 0x65, 0x6e, 0x73, 0x63, 0x2e, 0x2d, 0x69, 0x6e, 0x69, 0x77, 0x6e, 0x65, 0x6d, 0x2d, 0x72, 0x6e, 0x74, 0x6e, 0x40, 0x73, 0x2d, 0x74, 0x74, 0x65, 0x72, 0x2d, 0x2d, 0x69, 0x73, 0x70,
        0x69, 0x6c, 0x72, 0x76, 0x6d, 0x74, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x63, 0x69, 0x65, 0x65, 0x72, 0x6f, 0x6e, 0x72, 0x72, 0x6c, 0x6e, 0x6e, 0x65, 0x6d, 0x74, 0x6c, 0x74, 0x65, 0x69, 0x2d, 0x6f, 0x69, 0x2e, 0x6e, 0x63, 0x65, 0x6c, 0x40, 0x70, 0x2d, 0x2d, 0x74, 0x73, 0x74, 0x40, 0x72, 0x74, 0x6c, 0x72, 0x6e, 0x6f, 0x73, 0x65, 0x74, 0x6d, 0x69, 0x32, 0x72, 0x65, 0x77, 0x6e, 0x76, 0x74, 0x73, 0x2d, 0x72, 0x6e, 0x69, 0x73, 0x40, 0x36, 0x2d, 0x6d, 0x2e, 0x65, 0x6d, 0x40, 0x69, 0x72, 0x72, 0x70, 0x65, 0x72, 0x76, 0x6c, 0x65, 0x76, 0x72, 0x65, 0x69, 0x65, 0x69, 0x6e, 0x72, 0x2d, 0x63, 0x72, 0x69, 0x6e, 0x72, 0x69, 0x6e, 0x69, 0x70, 0x6e, 0x2d, 0x69, 0x6c, 0x72, 0x2d, 0x65, 0x2d, 0x72, 0x6f, 0x65, 0x6e, 0x76, 0x6e, 0x40, 0x2d, 0x65, 0x72, 0x72, 0x6f, 0x6f, 0x72, 0x6c, 0x65, 0x74, 0x73, 0x72, 0x70, 0x77, 0x69, 0x69, 0x6d, 0x6c, 0x6d, 0x6e, 0x2d, 0x65, 0x65, 0x65, 0x74, 0x6c, 0x2d, 0x74, 0x6f, 0x2d, 0x74, 0x70, 0x72, 0x6e, 0x73, 0x72, 0x69, 0x72, 0x2e, 0x6d, 0x69, 0x65, 0x65, 0x32, 0x70, 0x6c, 0x6c, 0x65, 0x77, 0x2d, 0x72, 0x6f, 0x70, 0x76, 0x65, 0x2d, 0x72, 0x69, 0x6d, 0x72, 0x36, 0x40, 0x6d, 0x72, 0x6c, 0x6d, 0x77, 0x6c, 0x6e, 0x69, 0x72, 0x6d, 0x76, 0x73, 0x2e, 0x73, 0x72, 0x77, 0x73, 0x76, 0x2d, 0x73, 0x76, 0x6d, 0x76, 0x65, 0x69, 0x76, 0x63, 0x65, 0x72, 0x31, 0x72, 0x69, 0x76, 0x72, 0x65, 0x65, 0x2d, 0x73, 0x6d, 0x31, 0x72, 0x6e, 0x72, 0x2d, 0x2d, 0x36, 0x72, 0x73, 0x77, 0x2d, 0x77, 0x36, 0x76, 0x72, 0x6d, 0x65, 0x2d, 0x72, 0x70, 0x2d, 0x74, 0x32, 0x6c, 0x63, 0x6d, 0x6f, 0x6e, 0x2e, 0x2d, 0x69, 0x65, 0x73, 0x6d, 0x65, 0x73, 0x6e, 0x6d, 0x6c, 0x65, 0x6e, 0x72, 0x72, 0x72, 0x32, 0x70, 0x65, 0x73, 0x6c, 0x6d, 0x70, 0x6d, 0x72, 0x6f, 0x65, 0x6c, 0x76, 0x73, 0x63, 0x73, 0x65, 0x6c, 0x2d, 0x6e, 0x72, 0x65, 0x65, 0x72, 0x2d, 0x70, 0x6d, 0x69, 0x69, 0x65, 0x2d, 0x6c, 0x72, 0x69, 0x6c, 0x2d, 0x74, 0x65, 0x65, 0x69, 0x31, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x74, 0x73, 0x65, 0x32, 0x2d, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x65, 0x69, 0x73, 0x32, 0x6d, 0x65, 0x2d, 0x65, 0x74, 0x6d, 0x6d, 0x73, 0x76, 0x6c, 0x69, 0x65, 0x2d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x72, 0x74, 0x31, 0x2d, 0x76, 0x73, 0x2e, 0x2d, 0x2d, 0x72, 0x76, 0x77, 0x65, 0x72, 0x72, 0x40, 0x6e, 0x6c, 0x6d, 0x72, 0x74, 0x73, 0x72, 0x72, 0x65, 0x65, 0x2d, 0x6f, 0x74, 0x70, 0x63, 0xb8, 0xa1, 0x11, 0x6e, 0xd7, 0x74, 0x16, 0x7f, 0xb4, 0xba, 0x40, 0x93, 0x98, 0x00, 0x71, 0xcc, 0x42, 0xa7, 0x2f, 0x28, 0x69, 0xe7, 0x31, 0x48, 0x22, 0xa0, 0xe1, 0x45, 0xe3, 0xf7, 0x7f, 0x3a
    };
    uint8_t c2[] = {
        0x5b, 0x52, 0xf1, 0x2d, 0x94, 0xcb, 0xb0, 0x86, 0xd8, 0xd3, 0xe3, 0x20, 0x88, 0x47, 0xcf, 0x5a, 0x49, 0xd2, 0x11, 0x30, 0x92, 0x17, 0x8d, 0xf4, 0x99, 0xf7, 0x6c, 0x8a, 0xbc, 0xe7, 0x5c, 0x58, 0x6a, 0x65, 0xed, 0x81, 0xdc, 0xdd, 0xcf, 0x83, 0xcd, 0xa4, 0xed, 0xa2, 0x5e, 0x63, 0xd9, 0x98, 0xf6, 0x2e, 0x15, 0x76, 0x9a, 0xc8, 0x8c, 0x42, 0x54, 0x44, 0xf4, 0x47, 0xf5, 0x96, 0xc9, 0x6e, 0x23, 0x09, 0x1a, 0x0d, 0xe3, 0x04, 0xe6, 0xed, 0x48, 0x49, 0x62, 0x31, 0xe8, 0x36, 0x04, 0xed, 0xb9, 0xe7, 0xa6, 0x35, 0x4d, 0xcd, 0xe3, 0xfa, 0xa0, 0xc8, 0x34, 0xbd, 0x62, 0x7b, 0xbc, 0xbe, 0x1c, 0x5b, 0x69, 0x1f, 0x9c, 0x30, 0x20, 0x48, 0x52, 0xd1, 0xb6, 0x5e, 0xa2, 0x6e, 0x06, 0x94, 0x72, 0x10, 0x56, 0x7c, 0x94, 0xa5, 0xc0, 0xaa, 0xea, 0x48, 0x61, 0x03, 0x14, 0x94, 0x09, 0x77, 0xd9, 0xa7, 0xfe, 0x78, 0x17, 0x95, 0x4f, 0x7e, 0xb0, 0x32, 0x63, 0x02, 0x17, 0x47, 0x1e, 0x7d, 0xb2, 0x7d, 0xb5, 0xcb, 0x9f, 0x61, 0x65, 0xed, 0x03, 0xd2, 0xdb, 0xd1, 0xb3, 0xd6, 0x1a, 0xf5, 0x67, 0x0b, 0x8b, 0x6b, 0x44, 0xf2, 0x62, 0x42, 0xc2, 0x4d, 0xe1, 0x5c, 0xfe, 0xc6, 0x19, 0x2b, 0xfb, 0x03, 0x0f, 0x1b, 0x89, 0x08, 0x86, 0x40, 0xca, 0x45, 0x15, 0xda, 0x65, 0xcc, 0x73, 0x00, 0x49, 0x4e, 0x48, 0x21, 0x25, 0xc6, 0xde, 0x26, 0x21, 0x1d, 0xea, 0x3c, 0x11, 0xac, 0xef, 0x34, 0x4c, 0x96, 0xcc, 0x5e, 0x26, 0xf3, 0xcd, 0x70, 0x0d, 0x62, 0xea, 0x09, 0x35, 0x2b, 0x1e, 0x60, 0xe4, 0x76, 0xd3, 0x65, 0x01, 0x8c, 0xab, 0xd4, 0x89, 0xad, 0x81, 0x9d, 0x04, 0x01, 0xd5, 0x55, 0x3c, 0xcb, 0x32, 0xe1, 0xb5, 0xd4, 0xda, 0xb4, 0xa9, 0x01, 0xb2, 0x10, 0xc7, 0xb1, 0xa9, 0x54, 0x66, 0x1d, 0xcc, 0xff, 0x54, 0x0b, 0x84, 0x37, 0xe0, 0x3a, 0xa5, 0x68, 0x80, 0x87, 0xbc, 0x3c, 0x0f, 0xda, 0x7e, 0x3c, 0x23, 0xfc, 0xd8, 0xc5, 0x52, 0xf7, 0x22, 0x12, 0x05, 0x9c, 0x68, 0x39, 0xb1, 0xed, 0x26, 0x24, 0x2b, 0x7e, 0x0b, 0xaf, 0x9e, 0x97, 0x45, 0x7b, 0xa9, 0xbc, 0x48, 0x0e, 0x66, 0x93, 0x32, 0x0d, 0x6b, 0xd6, 0xf0, 0x4f, 0x54, 0x18, 0xcd, 0xc9, 0x8c, 0xce, 0xc4, 0xa2, 0xff, 0x1e, 0x69, 0x17, 0x7e, 0xf4, 0x99, 0x09, 0x68, 0xa1, 0x9e, 0x1f, 0xbf, 0x90, 0xdc, 0x77, 0x5d, 0x50, 0x2b, 0x0e, 0xff, 0x96, 0xdc, 0x21, 0x2e, 0x74, 0x22, 0x28, 0x88, 0xa0, 0x00, 0x32, 0x15, 0xb0, 0xfd, 0xb1, 0xc9, 0x75, 0xb3, 0x3c, 0xbd, 0x89, 0xc5, 0xa4, 0x48, 0x17, 0xa9, 0xc9, 0x50, 0x61, 0x0c, 0x35, 0x31, 0x55, 0x11, 0xe3, 0x23, 0xe9, 0x3e, 0x78, 0x25, 0xdc, 0x50, 0xe8, 0x23, 0x5f, 0xb7, 0x3f, 0xc7, 0xae, 0xf0, 0x82, 0x35, 0x46, 0x34, 0x63, 0xcc, 0x5d, 0x96, 0xb8, 0x6a, 0x7a, 0x7f, 0x54, 0x27, 0x1a, 0xa4, 0x63, 0xdd, 0xb0, 0xb6, 0x17, 0x08, 0xa1, 0x2e, 0x95, 0x9e, 0xd4, 0x9b, 0x71, 0x83, 0x81, 0x6c, 0xea, 0xab, 0x00, 0x2e, 0xca, 0x60, 0xc1, 0x4b, 0x83, 0xa7, 0xab, 0x47, 0xe8, 0x1b, 0x5a, 0x78, 0x4f, 0xec, 0xbd, 0x62, 0x94, 0x25, 0x75, 0x2e, 0x64, 0xe7, 0x70, 0x13, 0xac, 0xe9, 0x89, 0x4f, 0x1e, 0x79, 0xbc, 0x15, 0x0c, 0x8d, 0x40, 0xe8, 0x16, 0x31, 0x7c, 0xb8, 0xa5, 0xd7, 0x21, 0x39, 0x93, 0x9b, 0xe6, 0x05, 0x81, 0xb6, 0x20, 0xa8, 0x5d, 0x73, 0x58, 0x8b, 0x66, 0x92, 0xac, 0x23, 0xa0, 0xf4, 0x8c, 0xab, 0x58, 0xae, 0xb6, 0x9c, 0x3c, 0x4d, 0x77, 0x5f, 0xae, 0xe2, 0x57, 0x89, 0x8f, 0xe4, 0x68, 0x81, 0x24, 0x7d, 0x3b, 0x99, 0x46, 0x9f, 0x7b, 0x9d, 0xa6, 0xdd, 0x99, 0xcf, 0xc1, 0x79, 0x04, 0x95, 0xce, 0x96, 0x7a, 0xd9, 0xb5, 0x6e, 0xcf, 0xd1, 0x72, 0x18, 0x97, 0x76, 0xe2, 0xb7, 0x38, 0x1e, 0x24, 0x0b, 0x09, 0x00, 0x8b, 0x28, 0x5d, 0xf8, 0xd0, 0x50, 0x7f, 0xeb, 0x3b, 0x37, 0x61, 0x0b, 0xd3, 0xff, 0x65, 0x7d, 0x88, 0x1e, 0x1d, 0xbb, 0x6c, 0xf5, 0xf8, 0xf3, 0x2b, 0x51, 0xd9, 0x6d, 0xc9, 0xbe, 0xbe, 0xd1, 0x94, 0x0e, 0x58, 0x2a, 0x0a, 0xe4, 0xf8, 0x28, 0x26, 0xc3, 0x74, 0x87, 0xd3, 0x81, 0x48, 0x6e, 0x9b, 0xd5, 0xa1, 0x60, 0x87, 0xfc, 0x1b, 0x06, 0x33, 0x0d, 0x87, 0xfa, 0x9b, 0xf9, 0x73, 0x6b, 0x0c, 0xdf, 0xea, 0xee, 0x32, 0x78, 0xe0, 0xf8, 0x18, 0x3f, 0xc3, 0x3b, 0x12, 0x88, 0x0b, 0xb2, 0x4a, 0x52, 0x64, 0x4e, 0x58, 0x54, 0x82, 0x52, 0x61, 0x54, 0x28, 0x1b, 0xf7, 0x99, 0x06, 0xa2, 0xad, 0x04, 0x19, 0x9f, 0x2e, 0x34, 0xe6, 0xf0, 0xee, 0xeb, 0x93, 0x9a, 0x9c, 0x73, 0x86, 0x23, 0x6d, 0x5d, 0xae, 0x64, 0xec, 0x6f, 0xf9, 0x7c, 0xc7, 0x46, 0x96, 0xdb, 0x44, 0xf4, 0xab, 0xc9, 0x67, 0x61, 0xb8, 0xec, 0xf0, 0x99, 0xe0, 0x4d, 0x45, 0xed, 0xa3, 0x1c, 0xe9, 0x68, 0x31, 0x85, 0xa5, 0xa1, 0xba, 0x08, 0xdb, 0x3f, 0x84, 0x75, 0x70, 0x24, 0xcd, 0x49, 0xd4, 0x07, 0xa8, 0xaa, 0x52, 0xd9, 0x55, 0x68, 0x8f, 0x78, 0xd2, 0x5d, 0x46, 0x23, 0x60, 0x76, 0xe1, 0x22, 0xdc, 0x2a, 0xeb, 0xac, 0xbc, 0xeb, 0xd6, 0x4c, 0x0f, 0xb5, 0xcb, 0x47, 0xce, 0x43, 0x59, 0x1d, 0x3e, 0xfc, 0x7f, 0x7c, 0x93, 0x9e, 0xef, 0xcd, 0x79, 0x5c, 0x08, 0x8e, 0xeb, 0xa8, 0x98, 0x3e, 0x95, 0xd1, 0x36, 0x42, 0x57, 0xfd, 0x6d, 0xdc, 0xe0, 0xa3, 0x3f, 0x46, 0x32, 0xb7, 0xff, 0x00, 0x4f, 0x7b, 0x23, 0x4d, 0xd0, 0xe5, 0xdd, 0x40, 0xab, 0xb2, 0xcb, 0x45, 0x92, 0x76, 0x7c, 0x5b, 0x98, 0xc7, 0xc0, 0x54, 0x34, 0x94, 0x8e, 0xbb, 0x28, 0xcf, 0xba, 0xd9, 0xa0, 0xe6, 0xf3, 0x65, 0x61, 0xd7, 0x10, 0xd3, 0xeb, 0xce, 0x21, 0x6a, 0xca, 0x61, 0xe7, 0x81, 0x15, 0x18, 0x4e, 0x71, 0xb0, 0x99, 0x62, 0xd9, 0xeb, 0xd0, 0x8b, 0xe9, 0xdf, 0x6a, 0x6d, 0x59, 0x0b, 0x45, 0x93, 0x38, 0xfe, 0xe6, 0x6a, 0xd1, 0x5f, 0xb6, 0xe9, 0x86, 0x01, 0x38, 0xab, 0x59, 0x5c, 0xd7, 0xb7, 0xfa, 0x81, 0x8a, 0xbe, 0xdc, 0xeb, 0x50, 0x7d, 0x81, 0xfa, 0x1b, 0x8f, 0xce, 0x53, 0x38, 0xe4, 0x8a, 0x82, 0xbe, 0x7d, 0xdc, 0xd8, 0x57, 0x5a, 0x48, 0xa3, 0x38, 0x74, 0x8a, 0xac, 0xf2, 0xfd, 0xbf, 0xcc, 0xd8, 0x08, 0x4d, 0x3e, 0xae, 0xa9, 0x00, 0x66, 0x06, 0xcb, 0xf3,
        0x50, 0xcc, 0x52, 0xc7, 0x4b, 0x16, 0x33, 0xa5, 0xde, 0x20, 0xed, 0x6a, 0xa7, 0x58, 0x5e, 0x4e, 0x7e, 0x29, 0xab, 0xb9, 0x65, 0x9d, 0x17, 0xe0, 0x1e, 0x79, 0x77, 0xf6, 0x1e, 0xa0, 0xcb, 0x0c, 0xf7, 0xc0, 0xe4, 0xf6, 0x3b, 0x60, 0x81, 0xfe, 0xed, 0xd9, 0x42, 0xa9, 0x61, 0x9d, 0xa8, 0xd7, 0xe8, 0xaa, 0x97, 0xad, 0xbb, 0xba, 0x13, 0x6e, 0x05, 0xa5, 0xce, 0x7a, 0x65, 0x6f, 0x55, 0xe3, 0xcf, 0xbc, 0x67, 0x14, 0x64, 0x57, 0x9c, 0x46, 0x14, 0xd6, 0x1d, 0x39, 0x1c, 0xd3, 0xe8, 0x98, 0x20, 0x5a, 0x1a, 0x05, 0x3b, 0x27, 0xd5, 0x84, 0xca, 0xd4, 0x0b, 0xc4, 0x1e, 0xd8, 0x46, 0x29, 0x48, 0x95, 0xdb, 0xe5, 0x58, 0x8a, 0x51, 0xc7, 0x74, 0x7f, 0x53, 0xa8, 0xbb, 0x58, 0xc0, 0x5b, 0xe1, 0xa7, 0x27, 0x36, 0x6c, 0xa6, 0x70, 0xec, 0x88, 0xcd, 0x9a, 0x70, 0xe1, 0xa0, 0xc7, 0xdd, 0x60, 0x71, 0xf4, 0x2a, 0x51, 0x98, 0x8e, 0xab, 0xb8, 0x13, 0x03, 0x48, 0x5f, 0x44, 0xf8, 0x88, 0xd9, 0x7d, 0xd3, 0xf1, 0x5f, 0xc4, 0x2b, 0x44, 0x15, 0x57, 0x31, 0xa4, 0xa1, 0xdb, 0x6d, 0x2a, 0x5a, 0x5a, 0xf7, 0xde, 0xd5, 0x23, 0x38, 0x00, 0xe5, 0x5c, 0x55, 0xe7, 0x37, 0x9c, 0xcb, 0x8b, 0xc0, 0x33, 0x42, 0x68, 0x23, 0x84, 0x7d, 0x89, 0x9d, 0xae, 0x59, 0x18, 0xae, 0xea, 0x46, 0x3f, 0xac, 0x57, 0x0d, 0x5d, 0x49, 0x14, 0x50, 0xe5, 0x70, 0x17, 0x73, 0x09, 0x11, 0x93, 0x6b, 0x02, 0x22, 0xb7, 0x63, 0xc9, 0xe6, 0xa4, 0xe3, 0xb1, 0xf7, 0xa6, 0x58, 0x8d, 0x14, 0xa1, 0xda, 0x6a, 0xb9, 0x38, 0xf9, 0x20, 0x45, 0x8c, 0xe6, 0x32, 0x23, 0x9d, 0x5f, 0xba, 0xcb, 0xb4, 0x95, 0xf9, 0xa9, 0x5c, 0x60, 0x03, 0x5a, 0x8c, 0xa7, 0xb9, 0x65, 0xa8, 0x84, 0x38, 0xc0, 0x25, 0xe6, 0xa7, 0xc0, 0x3b, 0xbc, 0x11, 0xed, 0x0e, 0x9a, 0x6f, 0xfe, 0x61, 0x79, 0x86, 0x92, 0x3a, 0xce, 0xe0, 0xb7, 0x70, 0xad, 0xe0, 0xcc, 0x88, 0x47, 0xd9, 0x2a, 0x3d, 0x41, 0x06, 0x77, 0x41, 0xbe, 0x3f, 0x55, 0x31, 0x54, 0x10, 0x14, 0x5b, 0xdf, 0x88, 0xb2, 0x9f, 0xff, 0x11, 0xb8, 0x11, 0xdc, 0x5e, 0x64, 0xf9, 0x97, 0x8a, 0x26, 0x6a, 0x44, 0xb4, 0x83, 0x83, 0x9b, 0x81, 0xaa, 0xfd, 0xb5, 0x8b, 0x16, 0x18, 0x2e, 0x5c, 0xe4, 0x5b, 0x8f, 0xdd, 0x7c, 0x1f, 0x33, 0x2f, 0xef, 0x57, 0x8c, 0x6a, 0x3f, 0x3c, 0x19, 0x5e, 0x73, 0x64, 0xc5, 0xaf, 0x1d, 0xa1, 0xb4, 0x11, 0xee, 0x6b, 0x7e, 0x66, 0xfb, 0xaa, 0x03, 0x17, 0xe4, 0xc9, 0x90, 0x4b, 0xf2, 0x50, 0x55, 0x71, 0xad, 0x31, 0x71, 0x49, 0xd7, 0x80, 0xd1, 0xa5, 0x9f, 0x6d, 0x71, 0x28, 0x2b, 0x65, 0xcf, 0x8d, 0xb1, 0x2a, 0x33, 0xdc, 0x93, 0xff, 0x86, 0xd7, 0xa6, 0xd0, 0x46, 0x66, 0x32, 0x3d, 0x18, 0x8c, 0xd3, 0xda, 0xf6, 0x1b, 0xa0, 0x2d, 0x29, 0xfd, 0x8d, 0x57, 0x2c, 0x82, 0xed, 0x38, 0x4a, 0x6f, 0xc4, 0x3c, 0x9a, 0x61, 0xcb, 0xe5, 0xcf, 0xd3, 0x83, 0xa1, 0x91, 0x93, 0x0d, 0x75, 0xfd, 0x4e, 0x2c, 0x83, 0xa0, 0x85, 0x27, 0x13, 0x5a, 0x24, 0xbd, 0x08, 0x1e, 0xe9, 0xab, 0x92, 0x41, 0xc2, 0x3a, 0xa0, 0xe1, 0xfd, 0x00, 0xb9, 0xf8, 0xca, 0x0b, 0x1a, 0x8e, 0xf6, 0x27, 0x9f, 0x5a, 0xf0, 0x23, 0x07, 0xc8, 0xbf, 0xf6, 0x74, 0xe7, 0xf8, 0x67, 0xfc, 0x28, 0x4e, 0x6a, 0x6c, 0xc6, 0x83, 0xe3, 0xf0, 0x01, 0xe0, 0x0f, 0x2d, 0xdf, 0x9e, 0x4b, 0x8b, 0x06, 0x15, 0x4c, 0x9f, 0xdf, 0x55, 0x14, 0x44, 0xde, 0x34, 0x35, 0x5a, 0xcb, 0xe5, 0xa7, 0xb5, 0x7e, 0x00, 0x31, 0x98, 0x5f, 0x51, 0x11, 0x37, 0xe1, 0xd2, 0x99, 0x8f, 0x70, 0x13, 0x40, 0xa0, 0xbe, 0xf8, 0xde, 0xac, 0x37, 0x06, 0xb6, 0x26, 0xf3, 0xb1, 0x97, 0x0b, 0x85, 0x68, 0x09, 0xa4, 0xc8, 0x34, 0x0a, 0x41, 0x6e, 0xac, 0x1a, 0x5b, 0xe0, 0x91, 0x6f, 0xa3, 0x0a, 0xf6, 0x05, 0x37, 0x32, 0xe1, 0x8e, 0xd8, 0xed, 0x55, 0xa3, 0x54, 0x3f, 0x62, 0x95, 0x82, 0xcf, 0x0a, 0x19, 0xb4, 0x9f, 0x04, 0xcc, 0x86, 0x7e, 0xf1, 0xe5, 0x8b, 0x67, 0x73, 0xa2, 0x46, 0x4e, 0xf2, 0x98, 0x94, 0xb5, 0xeb, 0xa5, 0xbd, 0xcb, 0x66, 0x82, 0xe9, 0x87, 0xe9, 0xe3, 0x50, 0x55, 0x4b, 0xd6, 0x67, 0x30, 0xe1, 0x7c, 0x15, 0x77, 0x29, 0xfd, 0x85, 0x67, 0x5a, 0xc4, 0xd5, 0x69, 0xfa, 0xc7, 0x66, 0x66, 0x49, 0xf7, 0x5a, 0xcd, 0xd1, 0x81, 0x5c, 0x74, 0x8d, 0xbf, 0xc5, 0xc2, 0xff, 0x4d, 0x90, 0xe8, 0x8e, 0x05, 0x00, 0xff, 0x7a, 0xd7, 0xb2, 0x7a, 0xad, 0x8b, 0xd6, 0x4b, 0x52, 0x09, 0x50, 0x4b
    };

    if (true) {
        SrsHandshakeBytes bytes;
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c0c1());
        memcpy(bytes.c0c1, c0c1, 1537);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_s0s1s2());
        memcpy(bytes.s0s1s2, s0s1s2, 3073);
        ASSERT_EQ(ERROR_SUCCESS, bytes.create_c2());
        memcpy(bytes.c2, c2, 1536);
    
        MockEmptyIO eio;
        SrsSimpleHandshake hs;
        HELPER_EXPECT_SUCCESS(hs.handshake_with_client(&bytes, &eio));
        HELPER_EXPECT_SUCCESS(hs.handshake_with_server(&bytes, &eio));
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(s0s1s2, 3073);

        SrsRtmpClient r(&io);
        HELPER_EXPECT_SUCCESS(r.simple_handshake());
    }

    if (true) {
        MockBufferIO io;
        io.append(c0c1, 1537);
        io.append(c2, 1536);

        SrsRtmpServer r(&io);
        HELPER_EXPECT_SUCCESS(r.handshake());
    }
}

/**
* bytes equal utility
*/
VOID TEST(ProtocolHandshakeTest, BytesEqual)
{
    uint8_t a1[] = { 0x01 };
    uint8_t b1[] = { 0x02 };
    uint8_t a2[] = { 0x01, 0x02 };
    uint8_t b2[] = { 0x02, 0x03 };
    
    EXPECT_TRUE(srs_bytes_equals(NULL, NULL, 0));
    EXPECT_FALSE(srs_bytes_equals(a1, NULL, 1));
    EXPECT_FALSE(srs_bytes_equals(NULL, a1, 1));
    EXPECT_FALSE(srs_bytes_equals(a1, b1, 1));
    EXPECT_TRUE(srs_bytes_equals(a1, a1, 1));
    EXPECT_TRUE(srs_bytes_equals(a1, a2, 1));
    EXPECT_FALSE(srs_bytes_equals(a1, b2, 1));
}

/**
* generate tcUrl from ip/vhost/app/port
*/
VOID TEST(ProtocolUtilityTest, GenerateTcUrl)
{
    string ip; string vhost; string app; int port; string tcUrl; string param;
    
    ip = "127.0.0.1"; vhost = "__defaultVhost__"; app = "live"; port = 1935;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://127.0.0.1/live", tcUrl.c_str());
    
    ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 1935;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://demo/live", tcUrl.c_str());
    
    ip = "127.0.0.1"; vhost = "demo"; app = "live"; port = 19351;
    tcUrl = srs_generate_tc_url(ip, vhost, app, port);
    EXPECT_STREQ("rtmp://demo:19351/live", tcUrl.c_str());
}

/**
* shared ptr message array test
*/
VOID TEST(ProtocolMsgArrayTest, MessageArray)
{
    SrsMessageHeader header;
    SrsSharedPtrMessage msg;
    char* payload = new char[1024];
    EXPECT_TRUE(ERROR_SUCCESS == msg.create(&header, payload, 1024));
    EXPECT_EQ(0, msg.count());
    
    if (true) {
        SrsMessageArray arr(3);
        
        arr.msgs[0] = msg.copy();
        EXPECT_EQ(1, msg.count());
        
        arr.msgs[1] = msg.copy();
        EXPECT_EQ(2, msg.count());
        
        arr.msgs[2] = msg.copy();
        EXPECT_EQ(3, msg.count());
    }
    EXPECT_EQ(3, msg.count());
    
    if (true) {
        SrsMessageArray arr(3);
        
        arr.msgs[0] = msg.copy();
        EXPECT_EQ(4, msg.count());
        
        arr.msgs[2] = msg.copy();
        EXPECT_EQ(5, msg.count());
    }
    EXPECT_EQ(5, msg.count());
}

/**
* set/get timeout of protocol stack
*/
VOID TEST(ProtocolStackTest, ProtocolTimeout)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    EXPECT_TRUE(SRS_UTIME_NO_TIMEOUT == proto.get_recv_timeout());
    EXPECT_TRUE(SRS_UTIME_NO_TIMEOUT == proto.get_send_timeout());
    
    proto.set_recv_timeout(10 * SRS_UTIME_MILLISECONDS);
    EXPECT_TRUE(10 * SRS_UTIME_MILLISECONDS == proto.get_recv_timeout());
    
    proto.set_send_timeout(10 * SRS_UTIME_MILLISECONDS);
    EXPECT_TRUE(10 * SRS_UTIME_MILLISECONDS == proto.get_send_timeout());
}

/**
* get recv/send bytes of protocol stack.
*/
VOID TEST(ProtocolStackTest, ProtocolBytes)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    EXPECT_TRUE(0 == proto.get_recv_bytes());
    EXPECT_TRUE(0 == proto.get_send_bytes());
    
    SrsConnectAppPacket* pkt = new SrsConnectAppPacket();
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    EXPECT_TRUE(0 < proto.get_send_bytes());
}

/**
* recv a SrsConnectAppPacket packet.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x14, 0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/         0x0d, 0x76, 0x69, 0x64,
        0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x62, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, 0x73, 0x2e,
        0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73,
        0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x6d, 0x6c,
        0x3f, 0x76, 0x68, 0x6f, 0x73, 0x74, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x3d, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x26, 0x73, 0x65, 0x72,
        0x76, 0x65, 0x72, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x70, 0x6f, 0x72, 0x74,
        0xC3, /*next chunk.*/         0x3d, 0x31, 0x39, 0x33,
        0x35, 0x00, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
        0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsConnectAppPacket* spkt = dynamic_cast<SrsConnectAppPacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
}

// for librtmp, if ping, it will send a fresh stream with fmt=1,
// 0x42             where: fmt=1, cid=2, protocol contorl user-control message
// 0x00 0x00 0x00   where: timestamp=0
// 0x00 0x00 0x06   where: payload_length=6
// 0x04             where: message_type=4(protocol control user-control message)
// 0x00 0x06            where: event Ping(0x06)
// 0x00 0x00 0x0d 0x0f  where: event data 4bytes ping timestamp.
VOID TEST(ProtocolStackTest, ProtocolRecvMessageBug98)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        0x42, // 1bytes chunk header
        0x00, 0x00, 0x00, // timestamp=0
        0x00, 0x00, 0x06, // payload_length=6
        0x04, // message_type=4(protocol control user-control message)
        0x00, 0x06, // event Ping(0x06)
        0x00, 0x00, 0x0d, 0x0f // event data 4bytes ping timestamp.
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsUserControlPacket* spkt = dynamic_cast<SrsUserControlPacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
    EXPECT_EQ(SrcPCUCPingRequest, spkt->event_type);
    EXPECT_EQ(0x0d0f, spkt->event_data);
}

/**
* recv a SrsSetWindowAckSizePacket packet.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvAckSizeMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsSetWindowAckSizePacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x02,
        0x00, 0x00, 0x00,
        0x00, 0x00, 0x04,
        0x05,
        0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    
    SrsPacket* pkt = NULL;
    EXPECT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
    SrsAutoFree(SrsPacket, pkt);
    
    SrsSetWindowAckSizePacket* spkt = dynamic_cast<SrsSetWindowAckSizePacket*>(pkt);
    ASSERT_TRUE(NULL != spkt);
    EXPECT_EQ(0x0763, spkt->ackowledgement_window_size);
}

/**
* recv a video message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
}

/**
* recv a audio message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvAMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // audio message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x08, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_audio());
}

/**
* recv a video message in 2 chunk packets.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVMessage2Trunk)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
}

/**
* recv video and audio, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x00, // timestamp
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video and audio, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x00, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x00, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
    }
}

/**
* recv video, audio and video, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x12, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x22, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x12, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x22, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio and video, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x30, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt1)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x10, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x10, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=3 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0xC3,
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x30, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=1 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt11Length)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x20, // length, 288
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x20, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x20, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
}

/**
* recv video, audio, video and video, interlaced in chunks.
* the continue chunks use fmt=1, last video with fmt=2 header,
* last video changed length
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVAVVFmt12Length)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x03,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x10, // length, 272
            0x09, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x04,
            0x00, 0x00, 0x15, // timestamp
            0x00, 0x00, 0x90, // length, 144
            0x08, // message_type
            0x01, 0x00, 0x00, 0x00, // stream_id
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x43,
            0x00, 0x00, 0x10, // timestamp
            0x00, 0x01, 0x20, // length, 288
            0x09, // message_type
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // audio message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC4, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#1
    if (true) {
        uint8_t data[] = {
            // 12bytes header, 1byts chunk header, 11bytes msg heder
            0x83,
            0x00, 0x00, 0x20, // timestamp
            // msg payload start
            0x02, 0x00, 0x07, 0x63,
            0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
            0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
            0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
            0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
            0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
            0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
            0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
            0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#2
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
            0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
            0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
            0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
            0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    // video message, chunk#3
    if (true) {
        uint8_t data[] = {
            0xC3, /*next chunk.*/
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
            0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
        };
        bio.in_buffer.append((char*)data, sizeof(data));
    }
    
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x10, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x110, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_audio());
        EXPECT_EQ(0x15, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x20, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_video());
        EXPECT_EQ(0x40, msg->header.timestamp);
        EXPECT_EQ(0x01, msg->header.stream_id);
        EXPECT_EQ(0x120, msg->header.payload_length);
    }
}

/**
* recv video, with extended timestamp.
* small timestamp < 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x00, 0x00, 0x10, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x10, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* big timestamp > 0xffffff
*/
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x7f, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp.
* always use 31bits timestamp.
*/
// always use 31bits timestamp, for some server may use 32bits extended timestamp.
VOID TEST(ProtocolStackTest, ProtocolRecvExtTimeMessage3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0xff, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // always use 31bits timestamp
    EXPECT_EQ(0x7f010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* always send extended timestamp in 0xCX chunk packets.
*/
/**
* RTMP specification and ffmpeg/librtmp is false,
* but, adobe changed the specification, so flash/FMLE/FMS always true.
* default to true to support flash/FMLE/FMS.
* 
* ffmpeg/librtmp may donot send this filed, need to detect the value.
* @see also: http://blog.csdn.net/win_lin/article/details/13363699
* compare to the chunk timestamp, which is set by chunk message header
* type 0,1 or 2.
*
* @remark, nginx send the extended-timestamp in sequence-header,
* and timestamp delta in continue C1 chunks, and so compatible with ffmpeg,
* that is, there is no continue chunks and extended-timestamp in nginx-rtmp.
*
* @remark, srs always send the extended-timestamp, to keep simple,
* and compatible with adobe products.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC3,
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC3,
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 0xCX with extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* recv video, with extended timestamp, in 2 chunks packet.
* never send extended timestamp in 0xCX chunk packets.
*/
// FFMPEG/librtmp, RTMP specification standard protocol.
VOID TEST(ProtocolStackTest, ProtocolRecvVExtTime2Trunk2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0xff, 0xff, 0xff, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        0x00, 0x01, 0x02, 0x03, // extended timestamp
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC3,
        /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC3,
        /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 0xCX without extended timestamp.
    EXPECT_EQ(0x00010203, msg->header.timestamp);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, min chunk id is 2.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x02,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC2, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC2, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), min is 2
    EXPECT_EQ(0x02, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, cid in 2-63
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x09,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC9, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC9, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), cid in 2-63
    EXPECT_EQ(0x09, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 1B chunk header, max chunk id is 63.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid1BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x3F,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 1B cid(6bits), max is 63
    EXPECT_EQ(0x3F, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, min chunk id is 64.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x00,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x00, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x00, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), min is 64
    EXPECT_EQ(64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, cid in 64-319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), cid in 64-319
    EXPECT_EQ(0x10+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, cid in 64-319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BNormal2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0x11,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0x11, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0x11, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(8bits), cid in 64-319
    EXPECT_EQ(0x11+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 2B chunk header, max chunk id is 319.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid2BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x00, 0xFF,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC0, 0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC0, 0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(68bits), max is 319
    EXPECT_EQ(319, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, min chunk id is 64.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMin)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x00, 0x00,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x00, 0x00, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x00, 0x00, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), min is 64
    EXPECT_EQ(64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x00, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x00, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x00, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x10*256+64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal2)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x01, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x01, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x01, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x01 + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal3)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0xFF, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0xFF, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0xFF, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0xFF + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, cid in 64-65599, greater than 319
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BNormal4)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0x02, 0x10,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0x02, 0x10, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0x02, 0x10, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 3B cid(16bits), cid in 64-65599
    EXPECT_EQ(0x02 + (0x10*256) + 64, msg->header.perfer_cid);
}

/**
* a video message, in 2 chunks packet.
* use 3B chunk header, max chunk id is 65599.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvVCid3BMax)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x01, 0xFF, 0xFF,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        // chunk #2
        0xC1, 0xFF, 0xFF, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        // chunk #2
        0xC1, 0xFF, 0xFF, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // 2B cid(16bits), max is 65599
    EXPECT_EQ(65599, msg->header.perfer_cid);
}

/**
* recv a zero length video message.
*/
VOID TEST(ProtocolStackTest, ProtocolRecvV0LenMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // video #1
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x00, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        
        // video #2
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x00, 0x04, // length
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x00, 0x00, 0x07, 0x63
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
    SrsAutoFree(SrsCommonMessage, msg);
    EXPECT_TRUE(msg->header.is_video());
    // protocol stack will ignore the empty video message.
    EXPECT_EQ(4, msg->header.payload_length);
}

/**
* send a video message
*/
VOID TEST(ProtocolStackTest, ProtocolSendVMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    
    SrsCommonMessage* msg = new SrsCommonMessage();
    msg->size = sizeof(data);
    msg->payload = new char[msg->size];
    memcpy(msg->payload, data, msg->size);
    
    SrsSharedPtrMessage m;
    ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 0));
    EXPECT_EQ(16, bio.out_buffer.length());
}

/**
* send a SrsCallPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCallPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsCallPacket* pkt = new SrsCallPacket();
    pkt->command_name = "my_call";
    pkt->command_object = SrsAmf0Any::null();
    pkt->arguments = args;
    
    args->set("video_id", SrsAmf0Any::number(100));
    args->set("url", SrsAmf0Any::str("http://ossrs.net/api/v1/videos/100"));
    args->set("date", SrsAmf0Any::str("2014-07-11 16:20:10.2984"));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x6d,
        0x79, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
        0x5f, 0x69, 0x64, 0x00, 0x40, 0x59, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,
        0x6c, 0x02, 0x00, 0x22, 0x68, 0x74, 0x74, 0x70,
        0x3a, 0x2f, 0x2f, 0x6f, 0x73, 0x73, 0x72, 0x73,
        0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69,
        0x2f, 0x76, 0x31, 0x2f, 0x76, 0x69, 0x64, 0x65,
        0x6f, 0x73, 0x2f, 0x31, 0x30, 0x30, 0x00, 0x04,
        0x64, 0x61, 0x74, 0x65, 0x02, 0x00, 0x18, 0x32,
        0x30, 0x31, 0x34, 0x2d, 0x30, 0x37, 0x2d, 0x31,
        0x31, 0x20, 0x31, 0x36, 0x3a, 0x32, 0x30, 0x3a,
        0x31, 0x30, 0x2e, 0x32, 0x39, 0x38, 0x34, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsCallResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCallResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsCallResPacket* pkt = new SrsCallResPacket(0);
    pkt->command_name = "_result";
    pkt->command_object = SrsAmf0Any::null();
    pkt->response = args;
    
    args->set("video_id", SrsAmf0Any::number(100));
    args->set("url", SrsAmf0Any::str("http://ossrs.net/api/v1/videos/100"));
    args->set("date", SrsAmf0Any::str("2014-07-11 16:20:10.2984"));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x5f,
        0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x08, 0x76, 0x69, 0x64, 0x65, 0x6f,
        0x5f, 0x69, 0x64, 0x00, 0x40, 0x59, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,
        0x6c, 0x02, 0x00, 0x22, 0x68, 0x74, 0x74, 0x70,
        0x3a, 0x2f, 0x2f, 0x6f, 0x73, 0x73, 0x72, 0x73,
        0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x70, 0x69,
        0x2f, 0x76, 0x31, 0x2f, 0x76, 0x69, 0x64, 0x65,
        0x6f, 0x73, 0x2f, 0x31, 0x30, 0x30, 0x00, 0x04,
        0x64, 0x61, 0x74, 0x65, 0x02, 0x00, 0x18, 0x32,
        0x30, 0x31, 0x34, 0x2d, 0x30, 0x37, 0x2d, 0x31,
        0x31, 0x20, 0x31, 0x36, 0x3a, 0x32, 0x30, 0x3a,
        0x31, 0x30, 0x2e, 0x32, 0x39, 0x38, 0x34, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsCreateStreamPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsCreateStreamPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket();
    pkt->command_object = SrsAmf0Any::null();
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x63,
        0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x40, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x05
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsFMLEStartPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsFMLEStartPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket();
    pkt->command_name = "FMLEStart";
    pkt->command_object = SrsAmf0Any::null();
    pkt->stream_name = "livestream";
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x46,
        0x4d, 0x4c, 0x45, 0x53, 0x74, 0x61, 0x72, 0x74,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x05, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76,
        0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsFMLEStartResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsFMLEStartResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(1);
    pkt->command_name = "FMLEStart";
    pkt->command_object = SrsAmf0Any::null();
    pkt->args = args;
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x46,
        0x4d, 0x4c, 0x45, 0x53, 0x74, 0x61, 0x72, 0x74,
        0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x05, 0x06
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsPublishPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsPublishPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsPublishPacket* pkt = new SrsPublishPacket();
    pkt->command_name = "publish";
    pkt->command_object = SrsAmf0Any::null();
    pkt->stream_name = "livestream";
    pkt->type = "live";
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x70,
        0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73,
        0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00, 0x04,
        0x6c, 0x69, 0x76, 0x65
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsPlayResPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsPlayResPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    SrsPlayResPacket* pkt = new SrsPlayResPacket();
    pkt->command_name = "_result";
    pkt->command_object = SrsAmf0Any::null();
    pkt->desc = args;
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x5f,
        0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
        0x03, 0x00, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76, 0x65,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x00, 0x05,
        0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnBWDonePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnBWDonePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket();
    pkt->command_name = "onBWDone";
    pkt->args = SrsAmf0Any::null();
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x6f,
        0x6e, 0x42, 0x57, 0x44, 0x6f, 0x6e, 0x65, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnStatusCallPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnStatusCallPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
    pkt->command_name = "onStatus";
    pkt->args = SrsAmf0Any::null();
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x6f,
        0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05, 0x03, 0x00, 0x06, 0x73, 0x74, 0x72, 0x65,
        0x61, 0x6d, 0x02, 0x00, 0x0a, 0x6c, 0x69, 0x76,
        0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x00,
        0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsBandwidthPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsBandwidthPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsBandwidthPacket* pkt = new SrsBandwidthPacket();
    pkt->command_name = "startPublish";
    pkt->args = SrsAmf0Any::null();
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x14,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x73,
        0x74, 0x61, 0x72, 0x74, 0x50, 0x75, 0x62, 0x6c,
        0x69, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x00, 0x06,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00,
        0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x05, 0x73, 0x74, 0x61,
        0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnStatusDataPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnStatusDataPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("stream" , SrsAmf0Any::str("livestream"));
    args->set("start" , SrsAmf0Any::number(0));
    
    SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket();
    pkt->command_name = "onData";
    pkt->data = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x6f,
        0x6e, 0x44, 0x61, 0x74, 0x61, 0x03, 0x00, 0x06,
        0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x02, 0x00,
        0x0a, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72,
        0x65, 0x61, 0x6d, 0x00, 0x05, 0x73, 0x74, 0x61,
        0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSampleAccessPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSampleAccessPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket();
    pkt->command_name = "|RtmpSampleAccess";
    pkt->video_sample_access = true;
    pkt->audio_sample_access = true;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11, 0x7c,
        0x52, 0x74, 0x6d, 0x70, 0x53, 0x61, 0x6d, 0x70,
        0x6c, 0x65, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
        0x01, 0x01, 0x01, 0x01
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsOnMetaDataPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsOnMetaDataPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAmf0Object* args = SrsAmf0Any::object();
    
    args->set("width" , SrsAmf0Any::number(1024));
    args->set("height" , SrsAmf0Any::number(576));
    
    SrsOnMetaDataPacket* pkt = new SrsOnMetaDataPacket();
    pkt->name = "onMetaData";
    pkt->metadata = args;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x12,
        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0a, 0x6f,
        0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74,
        0x61, 0x03, 0x00, 0x05, 0x77, 0x69, 0x64, 0x74,
        0x68, 0x00, 0x40, 0x90, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x06, 0x68, 0x65, 0x69, 0x67,
        0x68, 0x74, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsSetWindowAckSizePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetWindowAckSizePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
    pkt->ackowledgement_window_size = 102400;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    uint8_t buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), (char*)buf, sizeof(buf)));
}

/**
* send a SrsAcknowledgementPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsAcknowledgementPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket();
    pkt->sequence_number = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x03,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSetChunkSizePacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetChunkSizePacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket();
    pkt->chunk_size = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsSetPeerBandwidthPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsSetPeerBandwidthPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket();
    pkt->type = SrsPeerBandwidthSoft;
    pkt->bandwidth = 1024;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x06,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
        0x01
    };
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
}

/**
* send a SrsUserControlPacket packet
*/
VOID TEST(ProtocolStackTest, ProtocolSendSrsUserControlPacket)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    SrsUserControlPacket* pkt = new SrsUserControlPacket();
    pkt->event_type = SrcPCUCSetBufferLength;
    pkt->event_data = 0x01;
    pkt->extra_data = 0x10;
    
    EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    char buf[] = {
        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x04,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
        0x00, 0x01, 0x00, 0x00, 0x00, 0x10
    };
    
    EXPECT_TRUE(srs_bytes_equals(bio.out_buffer.bytes(), buf, sizeof(buf)));
    EXPECT_TRUE(true);
}

/**
* recv video message from multiple chunks,
* the next chunks must not be fmt=0 which means new msg.
*/
// when exists cache msg, means got an partial message,
// the fmt must not be type0 which means new message.
VOID TEST(ProtocolStackTest, ProtocolRecvVMessageFmtInvalid)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // video message
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03,
        0x00, 0x00, 0x00, // timestamp
        0x00, 0x01, 0x10, // length, 272
        0x09, // message_type
        0x00, 0x00, 0x00, 0x00, // stream_id
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0x03, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    EXPECT_FALSE(ERROR_SUCCESS == proto.recv_message(&msg));
}

/**
* when recv bytes archive the ack-size, server must response a acked-size auto.
*/
VOID TEST(ProtocolStackTest, ProtocolAckSizeFlow)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    if (true) {
        SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket();
        pkt->ackowledgement_window_size = 512;
        
        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    }
    
    if (true) {
        SrsCommonMessage* msg = new SrsCommonMessage();
        msg->header.payload_length = msg->size = 4096;
        msg->payload = new char[msg->size];
        
        msg->header.message_type = 9;
        EXPECT_TRUE(msg->header.is_video());
    
        SrsSharedPtrMessage m;
        ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));

        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 1));
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    
    // recv SrsSetWindowAckSizePacket
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_window_ackledgement_size());
    }
    // recv video
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_video());
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv auto send acked size. #1
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_ackledgement());
    }
    
    // send again
    if (true) {
        SrsCommonMessage* msg = new SrsCommonMessage();
        msg->header.payload_length = msg->size = 4096;
        msg->payload = new char[msg->size];
        
        msg->header.message_type = 9;
        EXPECT_TRUE(msg->header.is_video());
    
        SrsSharedPtrMessage m;
        ASSERT_TRUE(ERROR_SUCCESS == m.create(msg));

        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_message(m.copy(), 1));
    }
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv video
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_video());
    }
    
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv auto send acked size. #2
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_ackledgement());
    }
}

/**
* when recv ping message, server will response it auto.
*/
VOID TEST(ProtocolStackTest, ProtocolPingFlow)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // ping request
    if (true) {
        SrsUserControlPacket* pkt = new SrsUserControlPacket();
        pkt->event_type = SrcPCUCPingRequest;
        pkt->event_data = 0x3456;
        EXPECT_TRUE(ERROR_SUCCESS == proto.send_and_free_packet(pkt, 0));
    }
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv ping
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        EXPECT_TRUE(msg->header.is_user_control_message());
    }
    
    // recv the server auto send ping response message
    // copy output to input
    if (true) {
        bio.in_buffer.append(bio.out_buffer.bytes(), bio.out_buffer.length());
        bio.out_buffer.erase(bio.out_buffer.length());
    }
    // recv ping
    if (true) {
        SrsCommonMessage* msg = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.recv_message(&msg));
        SrsAutoFree(SrsCommonMessage, msg);
        ASSERT_TRUE(msg->header.is_user_control_message());
        
        SrsPacket* pkt = NULL;
        ASSERT_TRUE(ERROR_SUCCESS == proto.decode_message(msg, &pkt));
        SrsUserControlPacket* spkt = dynamic_cast<SrsUserControlPacket*>(pkt);
        ASSERT_TRUE(spkt != NULL);
        
        EXPECT_TRUE(SrcPCUCPingResponse == spkt->event_type);
        EXPECT_TRUE(0x3456 == spkt->event_data);
    }
}

/**
* expect specified message
*/
VOID TEST(ProtocolStackTest, ProtocolExcpectMessage)
{
    MockBufferIO bio;
    SrsProtocol proto(&bio);
    
    // packet is SrsConnectAppPacket
    uint8_t data[] = {
        // 12bytes header, 1byts chunk header, 11bytes msg heder
        0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x14, 0x00, 0x00, 0x00, 0x00,
        // msg payload start
        0x02, 0x00, 0x07, 0x63,
        0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x00, 0x03, 0x61, 0x70, 0x70, 0x02, 0x00, 0x04, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x08, 0x66, 0x6c,
        0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0d, 0x57, 0x49, 0x4e, 0x20, 0x31, 0x32, 0x2c,
        0x30, 0x2c, 0x30, 0x2c, 0x34, 0x31, 0x00, 0x06, 0x73, 0x77, 0x66, 0x55, 0x72, 0x6c, 0x02, 0x00,
        0x51, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72,
        0x73, 0x2e, 0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65,
        0x72, 0x73, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x72, 0x65,
        0x6c, 0x65, 0x61, 0x73, 0x65, 0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c,
        0xC3, /*next chunk.*/         0x61, 0x79, 0x65, 0x72,
        0x2e, 0x73, 0x77, 0x66, 0x3f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31, 0x2e,
        0x32, 0x33, 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x14, 0x72, 0x74, 0x6d, 0x70,
        0x3a, 0x2f, 0x2f, 0x64, 0x65, 0x76, 0x3a, 0x31, 0x39, 0x33, 0x35, 0x2f, 0x6c, 0x69, 0x76, 0x65,
        0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69,
        0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x6d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xab, 0xee,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
        0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xC3, /*next chunk.*/         0x0d, 0x76, 0x69, 0x64,
        0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x02, 0x00, 0x62, 0x68,
        0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x73, 0x73, 0x72, 0x73, 0x2e,
        0x6e, 0x65, 0x74, 0x3a, 0x38, 0x30, 0x38, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73,
        0x2f, 0x73, 0x72, 0x73, 0x5f, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x6d, 0x6c,
        0x3f, 0x76, 0x68, 0x6f, 0x73, 0x74, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x73, 0x74, 0x72, 0x65, 0x61,
        0x6d, 0x3d, 0x6c, 0x69, 0x76, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x26, 0x73, 0x65, 0x72,
        0x76, 0x65, 0x72, 0x3d, 0x64, 0x65, 0x76, 0x26, 0x70, 0x6f, 0x72, 0x74,
        0xC3, /*next chunk.*/         0x3d, 0x31, 0x39, 0x33,
        0x35, 0x00, 0x0e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e,
        0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
    };
    bio.in_buffer.append((char*)data, sizeof(data));
    
    SrsCommonMessage* msg = NULL;
    SrsConnectAppPacket* pkt = NULL;
    ASSERT_TRUE(ERROR_SUCCESS == proto.expect_message<SrsConnectAppPacket>(&msg, &pkt));
    SrsAutoFree(SrsCommonMessage, msg);
    SrsAutoFree(SrsConnectAppPacket, pkt);
    ASSERT_TRUE(NULL != pkt);
}

VOID TEST(ProtocolRTMPTest, RTMPRequest)
{
    SrsRequest req;
    std::string param;
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://std.ossrs.net/live", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://s td.os srs.n et/li v e", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://s\ntd.o\rssrs.ne\nt/li\nve", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    req.stream = "livestream";
    srs_discovery_tc_url("rtmp://std.ossrs.net/live ", 
        req.schema, req.host, req.vhost, req.app, req.stream, req.port, param);
    req.strip();
    EXPECT_STREQ("rtmp", req.schema.c_str());
    EXPECT_STREQ("std.ossrs.net", req.host.c_str());
    EXPECT_STREQ("std.ossrs.net", req.vhost.c_str());
    EXPECT_STREQ("live", req.app.c_str());
    EXPECT_EQ(1935, req.port);
    
    EXPECT_TRUE(NULL == req.args);
    SrsRequest req1;
    req1.args = SrsAmf0Any::object();
    req.update_auth(&req1);
    EXPECT_TRUE(NULL != req.args);
    EXPECT_TRUE(req1.args != req.args);
}

VOID TEST(ProtocolRTMPTest, RTMPHandshakeBytes)
{
    MockBufferIO bio;
    SrsHandshakeBytes bytes;
    
    char hs[3073];
    bio.in_buffer.append(hs, sizeof(hs));
    bio.in_buffer.append(hs, sizeof(hs));
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_c0c1(&bio));
    EXPECT_TRUE(bytes.c0c1 != NULL);
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_c2(&bio));
    EXPECT_TRUE(bytes.c2 != NULL);
    
    EXPECT_TRUE(ERROR_SUCCESS == bytes.read_s0s1s2(&bio));
    EXPECT_TRUE(bytes.s0s1s2 != NULL);
}

struct MockStage
{
	http_parser parser;
	const char* at;
	size_t length;

	MockStage(http_parser* from);
};

MockStage::MockStage(http_parser* from)
{
	parser = *from;
	at = NULL;
	length = 0;
}

class MockParser
{
private:
	http_parser_settings settings;
	http_parser* parser;
	size_t parsed;
public:
	MockStage* message_begin;
	MockStage* url;
	MockStage* status;
	MockStage* header_field;
	MockStage* header_value;
	MockStage* headers_complete;
	MockStage* body;
	MockStage* message_complete;
	MockStage* chunk_header;
	MockStage* chunk_complete;
public:
	MockParser();
	virtual ~MockParser();
public:
	srs_error_t parse(string data);
private:
    static int on_message_begin(http_parser* parser);
    static int on_url(http_parser* parser, const char* at, size_t length);
    static int on_status(http_parser* parser, const char* at, size_t length);
    static int on_header_field(http_parser* parser, const char* at, size_t length);
    static int on_header_value(http_parser* parser, const char* at, size_t length);
    static int on_headers_complete(http_parser* parser);
    static int on_body(http_parser* parser, const char* at, size_t length);
    static int on_message_complete(http_parser* parser);
    static int on_chunk_header(http_parser* parser);
    static int on_chunk_complete(http_parser* parser);
};

MockParser::MockParser()
{
	parser = new http_parser();
	http_parser_init(parser, HTTP_REQUEST);
    parser->data = (void*)this;
    parsed = 0;

    memset(&settings, 0, sizeof(settings));
    settings.on_message_begin = on_message_begin;
    settings.on_url = on_url;
    settings.on_status = on_status;
    settings.on_header_field = on_header_field;
    settings.on_header_value = on_header_value;
    settings.on_headers_complete = on_headers_complete;
    settings.on_body = on_body;
    settings.on_message_complete = on_message_complete;
    settings.on_chunk_header = on_chunk_header;
    settings.on_chunk_complete = on_chunk_complete;

	message_begin = NULL;
	url = NULL;
	status = NULL;
	header_field = NULL;
	header_value = NULL;
	headers_complete = NULL;
	body = NULL;
	message_complete = NULL;
	chunk_header = NULL;
	chunk_complete = NULL;
}

MockParser::~MockParser()
{
	srs_freep(parser);

	srs_freep(message_begin);
	srs_freep(url);
	srs_freep(status);
	srs_freep(header_field);
	srs_freep(header_value);
	srs_freep(headers_complete);
	srs_freep(body);
	srs_freep(message_complete);
	srs_freep(chunk_header);
	srs_freep(chunk_complete);
}

int MockParser::on_message_begin(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->message_begin);
    obj->message_begin = new MockStage(parser);

    return 0;
}

int MockParser::on_url(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->url);
    obj->url = new MockStage(parser);
    obj->url->at = at;
    obj->url->length = length;

    return 0;
}

int MockParser::on_status(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->status);
    obj->status = new MockStage(parser);
    obj->status->at = at;
    obj->status->length = length;

    return 0;
}

int MockParser::on_header_field(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->header_field);
    obj->header_field = new MockStage(parser);
    obj->header_field->at = at;
    obj->header_field->length = length;

    return 0;
}

int MockParser::on_header_value(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->header_value);
    obj->header_value = new MockStage(parser);
    obj->header_value->at = at;
    obj->header_value->length = length;

    return 0;
}

int MockParser::on_headers_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->headers_complete);
    obj->headers_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_body(http_parser* parser, const char* at, size_t length)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->body);
    obj->body = new MockStage(parser);
    obj->body->at = at;
    obj->body->length = length;

    return 0;
}

int MockParser::on_message_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->message_complete);
    obj->message_complete = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_header(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->chunk_header);
    obj->chunk_header = new MockStage(parser);

    return 0;
}

int MockParser::on_chunk_complete(http_parser* parser)
{
    MockParser* obj = (MockParser*)parser->data;
    srs_assert(obj);

	srs_freep(obj->chunk_complete);
    obj->chunk_complete = new MockStage(parser);

    return 0;
}

srs_error_t MockParser::parse(string data)
{
	srs_error_t err = srs_success;

	const char* buf = (const char*)data.data();
	size_t size = (size_t)data.length();
	size_t nparsed = http_parser_execute(parser, &settings, buf, size);
	parsed = nparsed;

	if (nparsed != size) {
		return srs_error_new(-1, "nparsed=%d, size=%d", nparsed, size);
	}

	return err;
}

VOID TEST(ProtocolHTTPTest, HTTPParser)
{
	srs_error_t err;

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_TRUE(!parser.body);
		EXPECT_TRUE(parser.headers_complete);
		EXPECT_TRUE(!parser.message_complete);
	}

	if (true) {
		MockParser parser;
		// size = 75, nparsed = 75, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_TRUE(parser.body && 5 == parser.body->length);
		EXPECT_TRUE(parser.headers_complete);
		EXPECT_TRUE(parser.message_complete);
	}

	if (true) {
		MockParser parser;
		// size = 150, nparsed = 150, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
		EXPECT_EQ(150, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(5, (int)parser.parser->content_length);

		// size = 79, nparsed = 5, nread = 1, content_length = -1, Header("Content-Length", 5)
		HELPER_EXPECT_FAILED(parser.parse("elloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(5, (int)parser.parsed);
		EXPECT_EQ(1, (int)parser.parser->nread);
		EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 70, nparsed = 70, nread = 0, content_length = 5, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\n"));
		EXPECT_EQ(70, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(5, (int)parser.parser->content_length);

		// size = 80, nparsed = 70, nread = 0, content_length = 0, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("HelloGET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nWorld"));
		EXPECT_EQ(80, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(0, (int)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 73, nparsed = 73, nread = 0, content_length = 2, Header("Content-Length", 5)
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHel"));
		EXPECT_EQ(73, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
		EXPECT_EQ(2, (int)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 82, nparsed = 75, nread = 1, content_length = -1, Header("Content-Length", 5)
		HELPER_EXPECT_FAILED(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello World!"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(1, (int)parser.parser->nread);
		EXPECT_EQ(-1, (int64_t)parser.parser->content_length);
	}

	if (true) {
		MockParser parser;
		// size = 34, nparsed = 34, nread = 34
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHo"));
		EXPECT_EQ(34, (int)parser.parsed);
		EXPECT_EQ(34, (int)parser.parser->nread);

		// size = 41, nparsed = 41, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("st: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(41, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 41, nparsed = 41, nread = 41
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: oss"));
		EXPECT_EQ(41, (int)parser.parsed);
		EXPECT_EQ(41, (int)parser.parser->nread);

		// size = 34, nparsed = 34, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("rs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(34, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 48, nparsed = 48, nread = 48
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r"));
		EXPECT_EQ(48, (int)parser.parsed);
		EXPECT_EQ(48, (int)parser.parser->nread);

		// size = 27, nparsed = 27, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(27, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 68, nparsed = 68, nread = 68
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n"));
		EXPECT_EQ(68, (int)parser.parsed);
		EXPECT_EQ(68, (int)parser.parser->nread);

		// size = 7, nparsed = 7, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\r\nHello"));
		EXPECT_EQ(7, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 69, nparsed = 69, nread = 69
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r"));
		EXPECT_EQ(69, (int)parser.parsed);
		EXPECT_EQ(69, (int)parser.parser->nread);

		// size = 6, nparsed = 6, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("\nHello"));
		EXPECT_EQ(6, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 75, nparsed = 75, nread = 0
		HELPER_EXPECT_SUCCESS(parser.parse("GET /gslb/v1/versions HTTP/1.1\r\nHost: ossrs.net\r\nContent-Length: 5\r\n\r\nHello"));
		EXPECT_EQ(75, (int)parser.parsed);
		EXPECT_EQ(0, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// nparsed = 2, size = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 0, nparsed = 1, nread=2
		HELPER_EXPECT_FAILED(parser.parse(""));
		EXPECT_EQ(1, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 2, nparsed = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 1, nparsed = 0, nread = 3
		HELPER_EXPECT_FAILED(parser.parse("X"));
		EXPECT_EQ(0, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 2, nparsed = 2, nread = 2
		HELPER_EXPECT_SUCCESS(parser.parse("GE"));
		EXPECT_EQ(2, (int)parser.parsed);
		EXPECT_EQ(2, (int)parser.parser->nread);

		// size = 1, nparsed = 1, nread = 3
		HELPER_EXPECT_SUCCESS(parser.parse("T"));
		EXPECT_EQ(1, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}

	if (true) {
		MockParser parser;
		// size = 3, nparsed = 3, nread = 3
		HELPER_EXPECT_SUCCESS(parser.parse("GET"));
		EXPECT_EQ(3, (int)parser.parsed);
		EXPECT_EQ(3, (int)parser.parser->nread);
	}
}

VOID TEST(ProtocolHTTPTest, ParseHTTPMessage)
{
    srs_error_t err = srs_success;

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));

        // We should read body, or next parsing message will fail.
        // @see https://github.com/ossrs/srs/issues/1181
        EXPECT_FALSE(req->body_reader()->eof());
        srs_freep(req);

		// Got new packet, notice that previous body still exists in bio.
        bio.append("GET /gslb/v2/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");

        // Should fail because there is body which not read.
        // @see https://github.com/ossrs/srs/issues/1181
        HELPER_ASSERT_FAILED(hp.parse_message(&bio, &req));
        srs_freep(req);
    }

    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;

        bio.append("GET");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));

		// Should fail if not completed message.
        ISrsHttpMessage* req = NULL;
        HELPER_ASSERT_FAILED(hp.parse_message(&bio, &req));
        srs_freep(req);
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 5\r\n\r\nHello");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
        
        char v[64] = {0};
        HELPER_ASSERT_SUCCESS(req->body_reader()->read(v, sizeof(v), NULL));
        EXPECT_TRUE(string("Hello") == string(v));
        
        EXPECT_TRUE(req->body_reader()->eof());
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\nContent-Length: 0\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
    }
    
    if (true) {
        MockBufferIO bio;
        SrsHttpParser hp;
        
        bio.append("GET /gslb/v1/versions HTTP/1.1\r\n\r\n");
        HELPER_ASSERT_SUCCESS(hp.initialize(HTTP_REQUEST));
        
        ISrsHttpMessage* req = NULL;
        SrsAutoFree(ISrsHttpMessage, req);
        HELPER_ASSERT_SUCCESS(hp.parse_message(&bio, &req));
    }
}

VOID TEST(ProtocolKbpsTest, Connections)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000)->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000)->set_out(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_in(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(800, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(io, io);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 300s.
        clock->set_clock(330 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(330 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(800, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, Delta)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        // No data.
        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)conn;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
        
        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);
        
        // No data.
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        // No data.
        ISrsKbpsDelta* delta = (ISrsKbpsDelta*)conn;
        int64_t in, out;
        delta->remark(&in, &out);
        EXPECT_EQ(0, in);
        EXPECT_EQ(0, out);
        
        // 800kb.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        delta->remark(&in, &out);
        EXPECT_EQ(100 * 1000, in);
        EXPECT_EQ(100 * 1000, out);
        
        // Kbps without io, gather delta.
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(NULL, NULL);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        kbps->add_delta(30 * in, 30 * out);
        kbps->sample();
        
        EXPECT_EQ(800, kbps->get_recv_kbps());
        EXPECT_EQ(800, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
}

VOID TEST(ProtocolKbpsTest, RAWStatistic)
{
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        SrsKbps* conn = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, conn);
        conn->set_io(io, io);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        kbps->set_io(conn, conn);
        
        // No data, 0kbps.
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(0, kbps->get_send_kbps());
        EXPECT_EQ(0, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
        
        // 800kbps in 30s.
        clock->set_clock(30 * 1000 * SRS_UTIME_MILLISECONDS);
        io->set_out(30 * 100 * 1000);
        kbps->sample();
        
        EXPECT_EQ(0, kbps->get_recv_kbps());
        EXPECT_EQ(0, kbps->get_recv_kbps_30s());
        EXPECT_EQ(0, kbps->get_recv_kbps_5m());
        
        EXPECT_EQ(800, kbps->get_send_kbps());
        EXPECT_EQ(800, kbps->get_send_kbps_30s());
        EXPECT_EQ(0, kbps->get_send_kbps_5m());
    }
    
    if (true) {
        MockWallClock* clock = new MockWallClock();
        SrsAutoFree(MockWallClock, clock);
        
        SrsKbps* kbps = new SrsKbps(clock->set_clock(0));
        SrsAutoFree(SrsKbps, kbps);
        
        // No io, no data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());
        
        // With io, zero data.
        MockStatistic* io = new MockStatistic();
        SrsAutoFree(MockStatistic, io);
        
        kbps->set_io(io, io);
        EXPECT_EQ(0, kbps->get_recv_bytes());
        EXPECT_EQ(0, kbps->get_send_bytes());
        
        // With io with data.
        io->set_in(100 * 1000)->set_out(100 * 1000);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        // No io, cached data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        // Use the same IO, but as a fresh io.
        kbps->set_io(io, io);
        EXPECT_EQ(100 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(100 * 1000, kbps->get_send_bytes());
        
        io->set_in(150 * 1000)->set_out(150 * 1000);
        EXPECT_EQ(150 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(150 * 1000, kbps->get_send_bytes());
        
        // No io, cached data.
        kbps->set_io(NULL, NULL);
        EXPECT_EQ(150 * 1000, kbps->get_recv_bytes());
        EXPECT_EQ(150 * 1000, kbps->get_send_bytes());
    }
}

VOID TEST(ProtocolKbpsTest, WriteLargeIOVs)
{
    srs_error_t err;

    if (true) {
        iovec iovs[1];
        iovs[0].iov_base = (char*)"Hello";
        iovs[0].iov_len = 5;

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, 1, &nn));
        EXPECT_EQ(5, nn);
        EXPECT_EQ(5, io.sbytes);
    }

    if (true) {
        iovec iovs[1024];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[1025];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }

    if (true) {
        iovec iovs[4096];
        int nn_iovs = (int)(sizeof(iovs)/sizeof(iovec));
        for (int i = 0; i < nn_iovs; i++) {
            iovs[i].iov_base = (char*)"Hello";
            iovs[i].iov_len = 5;
        }

        MockBufferIO io;
        ssize_t nn = 0;
        HELPER_EXPECT_SUCCESS(srs_write_large_iovs(&io, iovs, nn_iovs, &nn));
        EXPECT_EQ(5 * nn_iovs, nn);
        EXPECT_EQ(5 * nn_iovs, io.sbytes);
    }
}

